KInit
klauncher.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 1999 Waldo Bastian <bastian@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #define QT_NO_CAST_FROM_ASCII 00021 00022 #include "klauncher.h" 00023 #include "klauncher_cmds.h" 00024 #include "klauncher_adaptor.h" 00025 00026 #include <config.h> 00027 00028 #include <stdio.h> 00029 #include <unistd.h> 00030 #include <stdlib.h> 00031 #include <errno.h> 00032 #include <signal.h> 00033 #include <sys/time.h> 00034 00035 #ifdef Q_WS_X11 00036 #include <kstartupinfo.h> 00037 #include <X11/Xlib.h> 00038 #endif 00039 00040 #include <QtCore/QFile> 00041 00042 #include <kconfig.h> 00043 #include <kdebug.h> 00044 #include <kde_file.h> 00045 #include <klibrary.h> 00046 #include <klocale.h> 00047 #include <kprotocolmanager.h> 00048 #include <kprotocolinfo.h> 00049 #include <krun.h> 00050 #include <kstandarddirs.h> 00051 #include <ktemporaryfile.h> 00052 #include <kdesktopfile.h> 00053 #include <kurl.h> 00054 00055 #include <kio/global.h> 00056 #include <kio/connection.h> 00057 #include <kio/slaveinterface.h> 00058 00059 // Dispose slaves after being idle for SLAVE_MAX_IDLE seconds 00060 #define SLAVE_MAX_IDLE 30 00061 00062 // #define KLAUNCHER_VERBOSE_OUTPUT 00063 00064 static const char* const s_DBusStartupTypeToString[] = 00065 { "DBusNone", "DBusUnique", "DBusMulti", "DBusWait", "ERROR" }; 00066 00067 using namespace KIO; 00068 00069 IdleSlave::IdleSlave(QObject *parent) 00070 : QObject(parent) 00071 { 00072 QObject::connect(&mConn, SIGNAL(readyRead()), this, SLOT(gotInput())); 00073 // Send it a SLAVE_STATUS command. 00074 mConn.send( CMD_SLAVE_STATUS ); 00075 mPid = 0; 00076 mBirthDate = time(0); 00077 mOnHold = false; 00078 } 00079 00080 template<int T> struct PIDType { typedef pid_t PID_t; } ; 00081 template<> struct PIDType<2> { typedef qint16 PID_t; } ; 00082 template<> struct PIDType<4> { typedef qint32 PID_t; } ; 00083 00084 void 00085 IdleSlave::gotInput() 00086 { 00087 int cmd; 00088 QByteArray data; 00089 if (mConn.read( &cmd, data) == -1) 00090 { 00091 // Communication problem with slave. 00092 kError(7016) << "SlavePool: No communication with slave." << endl; 00093 deleteLater(); 00094 } 00095 else if (cmd == MSG_SLAVE_ACK) 00096 { 00097 deleteLater(); 00098 } 00099 else if (cmd != MSG_SLAVE_STATUS) 00100 { 00101 kError(7016) << "SlavePool: Unexpected data from slave." << endl; 00102 deleteLater(); 00103 } 00104 else 00105 { 00106 QDataStream stream( data ); 00107 PIDType<sizeof(pid_t)>::PID_t stream_pid; 00108 pid_t pid; 00109 QByteArray protocol; 00110 QString host; 00111 qint8 b; 00112 stream >> stream_pid >> protocol >> host >> b; 00113 pid = stream_pid; 00114 // Overload with (bool) onHold, (KUrl) url. 00115 if (!stream.atEnd()) 00116 { 00117 KUrl url; 00118 stream >> url; 00119 mOnHold = true; 00120 mUrl = url; 00121 } 00122 00123 mPid = pid; 00124 mConnected = (b != 0); 00125 mProtocol = QString::fromLatin1(protocol); 00126 mHost = host; 00127 emit statusUpdate(this); 00128 } 00129 } 00130 00131 void 00132 IdleSlave::connect(const QString &app_socket) 00133 { 00134 QByteArray data; 00135 QDataStream stream( &data, QIODevice::WriteOnly); 00136 stream << app_socket; 00137 mConn.send( CMD_SLAVE_CONNECT, data ); 00138 // Timeout! 00139 } 00140 00141 void 00142 IdleSlave::reparseConfiguration() 00143 { 00144 mConn.send( CMD_REPARSECONFIGURATION ); 00145 } 00146 00147 bool 00148 IdleSlave::match(const QString &protocol, const QString &host, bool needConnected) 00149 { 00150 if (mOnHold || protocol != mProtocol) { 00151 return false; 00152 } 00153 if (host.isEmpty()) { 00154 return true; 00155 } 00156 return (host == mHost) && (!needConnected || mConnected); 00157 } 00158 00159 bool 00160 IdleSlave::onHold(const KUrl &url) 00161 { 00162 if (!mOnHold) return false; 00163 return (url == mUrl); 00164 } 00165 00166 int 00167 IdleSlave::age(time_t now) 00168 { 00169 return (int) difftime(now, mBirthDate); 00170 } 00171 00172 static KLauncher* g_klauncher_self; 00173 00174 #ifndef USE_KPROCESS_FOR_KIOSLAVES 00175 KLauncher::KLauncher(int _kdeinitSocket) 00176 : QObject(0), 00177 kdeinitSocket(_kdeinitSocket) 00178 #else 00179 KLauncher::KLauncher() 00180 : QObject(0) 00181 #endif 00182 { 00183 #ifdef Q_WS_X11 00184 mCached_dpy = NULL; 00185 #endif 00186 Q_ASSERT( g_klauncher_self == NULL ); 00187 g_klauncher_self = this; 00188 00189 mAutoTimer.setSingleShot(true); 00190 new KLauncherAdaptor(this); 00191 QDBusConnection::sessionBus().registerObject(QLatin1String("/KLauncher"), this); // same as ktoolinvocation.cpp 00192 00193 connect(&mAutoTimer, SIGNAL(timeout()), this, SLOT(slotAutoStart())); 00194 connect(QDBusConnection::sessionBus().interface(), 00195 SIGNAL(serviceOwnerChanged(QString,QString,QString)), 00196 SLOT(slotNameOwnerChanged(QString,QString,QString))); 00197 00198 mConnectionServer.listenForRemote(); 00199 connect(&mConnectionServer, SIGNAL(newConnection()), SLOT(acceptSlave())); 00200 if (!mConnectionServer.isListening()) 00201 { 00202 // Severe error! 00203 qDebug("KLauncher: Fatal error, can't create tempfile!"); 00204 ::_exit(1); 00205 } 00206 00207 connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout())); 00208 00209 #ifndef USE_KPROCESS_FOR_KIOSLAVES 00210 kdeinitNotifier = new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read); 00211 connect(kdeinitNotifier, SIGNAL( activated( int )), 00212 this, SLOT( slotKDEInitData( int ))); 00213 kdeinitNotifier->setEnabled( true ); 00214 #endif 00215 lastRequest = 0; 00216 bProcessingQueue = false; 00217 00218 mSlaveDebug = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_DEBUG_WAIT")); 00219 if (!mSlaveDebug.isEmpty()) 00220 { 00221 qWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'", qPrintable(mSlaveDebug)); 00222 } 00223 mSlaveValgrind = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND")); 00224 if (!mSlaveValgrind.isEmpty()) 00225 { 00226 mSlaveValgrindSkin = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND_SKIN")); 00227 qWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'", qPrintable(mSlaveValgrind)); 00228 } 00229 #ifdef USE_KPROCESS_FOR_KIOSLAVES 00230 kDebug(7016) << "LAUNCHER_OK"; 00231 #else 00232 klauncher_header request_header; 00233 request_header.cmd = LAUNCHER_OK; 00234 request_header.arg_length = 0; 00235 write(kdeinitSocket, &request_header, sizeof(request_header)); 00236 #endif 00237 } 00238 00239 KLauncher::~KLauncher() 00240 { 00241 close(); 00242 g_klauncher_self = NULL; 00243 } 00244 00245 void KLauncher::close() 00246 { 00247 #ifdef Q_WS_X11 00248 if( mCached_dpy != NULL ) 00249 { 00250 XCloseDisplay( mCached_dpy ); 00251 mCached_dpy = NULL; 00252 } 00253 #endif 00254 } 00255 00256 void 00257 KLauncher::destruct() 00258 { 00259 if (g_klauncher_self) 00260 g_klauncher_self->close(); 00261 // We don't delete the app here, that's intentional. 00262 ::_exit(255); 00263 } 00264 00265 void KLauncher::setLaunchEnv(const QString &name, const QString &value) 00266 { 00267 #ifndef USE_KPROCESS_FOR_KIOSLAVES 00268 klauncher_header request_header; 00269 QByteArray requestData; 00270 requestData.append(name.toLocal8Bit()).append('\0').append(value.toLocal8Bit()).append('\0'); 00271 request_header.cmd = LAUNCHER_SETENV; 00272 request_header.arg_length = requestData.size(); 00273 write(kdeinitSocket, &request_header, sizeof(request_header)); 00274 write(kdeinitSocket, requestData.data(), request_header.arg_length); 00275 #else 00276 Q_UNUSED(name); 00277 Q_UNUSED(value); 00278 #endif 00279 } 00280 00281 #ifndef USE_KPROCESS_FOR_KIOSLAVES 00282 /* 00283 * Read 'len' bytes from 'sock' into buffer. 00284 * returns -1 on failure, 0 on no data. 00285 */ 00286 static int 00287 read_socket(int sock, char *buffer, int len) 00288 { 00289 ssize_t result; 00290 int bytes_left = len; 00291 while (bytes_left > 0) { 00292 // in case we get a request to start an application and data arrive 00293 // to kdeinitSocket at the same time, requestStart() will already 00294 // call slotKDEInitData(), so we must check there's still something 00295 // to read, otherwise this would block 00296 00297 // Same thing if kdeinit dies without warning. 00298 00299 fd_set in; 00300 timeval tm = { 30, 0 }; // 30 seconds timeout, so we're not stuck in case kdeinit dies on us 00301 FD_ZERO ( &in ); 00302 FD_SET( sock, &in ); 00303 select( sock + 1, &in, 0, 0, &tm ); 00304 if( !FD_ISSET( sock, &in )) { 00305 kDebug(7016) << "read_socket" << sock << "nothing to read, kdeinit4 must be dead"; 00306 return -1; 00307 } 00308 00309 result = read(sock, buffer, bytes_left); 00310 if (result > 0) 00311 { 00312 buffer += result; 00313 bytes_left -= result; 00314 } 00315 else if (result == 0) 00316 return -1; 00317 else if ((result == -1) && (errno != EINTR)) 00318 return -1; 00319 } 00320 return 0; 00321 } 00322 #endif 00323 00324 void 00325 KLauncher::slotKDEInitData(int) 00326 { 00327 #ifndef USE_KPROCESS_FOR_KIOSLAVES 00328 klauncher_header request_header; 00329 QByteArray requestData; 00330 00331 int result = read_socket(kdeinitSocket, (char *) &request_header, 00332 sizeof( request_header)); 00333 if (result == -1) 00334 { 00335 kDebug(7016) << "Exiting on read_socket errno:" << errno; 00336 KDE_signal( SIGHUP, SIG_IGN); 00337 KDE_signal( SIGTERM, SIG_IGN); 00338 destruct(); // Exit! 00339 } 00340 requestData.resize(request_header.arg_length); 00341 result = read_socket(kdeinitSocket, (char *) requestData.data(), 00342 request_header.arg_length); 00343 00344 processRequestReturn(request_header.cmd,requestData); 00345 #endif 00346 } 00347 00348 void KLauncher::processRequestReturn(int status, const QByteArray &requestData) 00349 { 00350 if (status == LAUNCHER_CHILD_DIED) 00351 { 00352 long *request_data; 00353 request_data = (long *) requestData.data(); 00354 processDied(request_data[0], request_data[1]); 00355 return; 00356 } 00357 if (lastRequest && (status == LAUNCHER_OK)) 00358 { 00359 long *request_data; 00360 request_data = (long *) requestData.data(); 00361 lastRequest->pid = (pid_t) (*request_data); 00362 kDebug(7016).nospace() << lastRequest->name << " (pid " << lastRequest->pid << 00363 ") up and running."; 00364 switch(lastRequest->dbus_startup_type) 00365 { 00366 case KService::DBusNone: 00367 lastRequest->status = KLaunchRequest::Running; 00368 break; 00369 case KService::DBusUnique: 00370 case KService::DBusWait: 00371 case KService::DBusMulti: 00372 lastRequest->status = KLaunchRequest::Launching; 00373 break; 00374 } 00375 lastRequest = 0; 00376 return; 00377 } 00378 if (lastRequest && (status == LAUNCHER_ERROR)) 00379 { 00380 lastRequest->status = KLaunchRequest::Error; 00381 kDebug(7016) << lastRequest->name << " failed." << endl; 00382 if (!requestData.isEmpty()) 00383 lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data()); 00384 lastRequest = 0; 00385 return; 00386 } 00387 00388 kWarning(7016)<< "Unexpected request return" << (unsigned int) status; 00389 } 00390 00391 void 00392 KLauncher::processDied(pid_t pid, long exitStatus) 00393 { 00394 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00395 kDebug(7016) << pid << "exitStatus=" << exitStatus; 00396 #else 00397 Q_UNUSED(exitStatus); 00398 // We should probably check the exitStatus for the uniqueapp case? 00399 #endif 00400 foreach (KLaunchRequest *request, requestList) 00401 { 00402 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00403 kDebug(7016) << " had pending request" << request->pid; 00404 #endif 00405 if (request->pid == pid) 00406 { 00407 if (request->dbus_startup_type == KService::DBusWait) 00408 request->status = KLaunchRequest::Done; 00409 else if ((request->dbus_startup_type == KService::DBusUnique) 00410 && QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { 00411 request->status = KLaunchRequest::Running; 00412 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00413 kDebug(7016) << pid << "running as a unique app"; 00414 #endif 00415 } else { 00416 request->status = KLaunchRequest::Error; 00417 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00418 kDebug(7016) << pid << "died, requestDone. status=" << request->status; 00419 #endif 00420 } 00421 requestDone(request); 00422 return; 00423 } 00424 } 00425 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00426 kDebug(7016) << "found no pending requests for PID" << pid; 00427 #endif 00428 } 00429 00430 static bool matchesPendingRequest(const QString& appId, const QString& pendingAppId) 00431 { 00432 // appId just registered, e.g. org.koffice.kword-12345 00433 // Let's see if this is what pendingAppId (e.g. org.koffice.kword or *.kword) was waiting for. 00434 00435 const QString newAppId = appId.left(appId.lastIndexOf(QLatin1Char('-'))); // strip out the -12345 if present. 00436 00437 //kDebug() << "appId=" << appId << "newAppId=" << newAppId << "pendingAppId=" << pendingAppId; 00438 00439 if (pendingAppId.startsWith(QLatin1String("*."))) { 00440 const QString pendingName = pendingAppId.mid(2); 00441 const QString appName = newAppId.mid(newAppId.lastIndexOf(QLatin1Char('.'))+1); 00442 //kDebug() << "appName=" << appName; 00443 return appName == pendingName; 00444 } 00445 00446 return newAppId == pendingAppId; 00447 } 00448 00449 void 00450 KLauncher::slotNameOwnerChanged(const QString &appId, const QString &oldOwner, 00451 const QString &newOwner) 00452 { 00453 Q_UNUSED(oldOwner); 00454 if (appId.isEmpty() || newOwner.isEmpty()) 00455 return; 00456 00457 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00458 kDebug(7016) << "new app" << appId; 00459 #endif 00460 foreach (KLaunchRequest *request, requestList) 00461 { 00462 if (request->status != KLaunchRequest::Launching) 00463 continue; 00464 00465 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00466 kDebug(7016) << "had pending request" << request->name << s_DBusStartupTypeToString[request->dbus_startup_type] << "dbus_name" << request->dbus_name << request->tolerant_dbus_name; 00467 #endif 00468 // For unique services check the requested service name first 00469 if (request->dbus_startup_type == KService::DBusUnique) { 00470 if ((appId == request->dbus_name) || // just started 00471 QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { // was already running 00472 request->status = KLaunchRequest::Running; 00473 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00474 kDebug(7016) << "OK, unique app" << request->dbus_name << "is running"; 00475 #endif 00476 requestDone(request); 00477 continue; 00478 } else { 00479 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00480 kDebug(7016) << "unique app" << request->dbus_name << "not running yet"; 00481 #endif 00482 } 00483 } 00484 00485 const QString rAppId = !request->tolerant_dbus_name.isEmpty() ? request->tolerant_dbus_name : request->dbus_name; 00486 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00487 //kDebug(7016) << "using" << rAppId << "for matching"; 00488 #endif 00489 if (rAppId.isEmpty()) 00490 continue; 00491 00492 if (matchesPendingRequest(appId, rAppId)) { 00493 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00494 kDebug(7016) << "ok, request done"; 00495 #endif 00496 request->dbus_name = appId; 00497 request->status = KLaunchRequest::Running; 00498 requestDone(request); 00499 continue; 00500 } 00501 } 00502 } 00503 00504 void 00505 KLauncher::autoStart(int phase) 00506 { 00507 if( mAutoStart.phase() >= phase ) 00508 return; 00509 mAutoStart.setPhase(phase); 00510 if (phase == 0) 00511 mAutoStart.loadAutoStartList(); 00512 mAutoTimer.start(0); 00513 } 00514 00515 void 00516 KLauncher::slotAutoStart() 00517 { 00518 KService::Ptr s; 00519 do 00520 { 00521 QString service = mAutoStart.startService(); 00522 if (service.isEmpty()) 00523 { 00524 // Done 00525 if( !mAutoStart.phaseDone()) 00526 { 00527 mAutoStart.setPhaseDone(); 00528 switch( mAutoStart.phase()) 00529 { 00530 case 0: 00531 emit autoStart0Done(); 00532 break; 00533 case 1: 00534 emit autoStart1Done(); 00535 break; 00536 case 2: 00537 emit autoStart2Done(); 00538 break; 00539 } 00540 } 00541 return; 00542 } 00543 s = new KService(service); 00544 } 00545 while (!start_service(s, QStringList(), QStringList(), "0", false, true, QDBusMessage())); 00546 // Loop till we find a service that we can start. 00547 } 00548 00549 void 00550 KLauncher::requestDone(KLaunchRequest *request) 00551 { 00552 if ((request->status == KLaunchRequest::Running) || 00553 (request->status == KLaunchRequest::Done)) 00554 { 00555 requestResult.result = 0; 00556 requestResult.dbusName = request->dbus_name; 00557 requestResult.error = QString::fromLatin1(""); // not null, cf assert further down 00558 requestResult.pid = request->pid; 00559 } 00560 else 00561 { 00562 requestResult.result = 1; 00563 requestResult.dbusName.clear(); 00564 requestResult.error = i18n("KDEInit could not launch '%1'.", request->name); 00565 if (!request->errorMsg.isEmpty()) 00566 requestResult.error += QString::fromLatin1(":\n") + request->errorMsg; 00567 requestResult.pid = 0; 00568 00569 #ifdef Q_WS_X11 00570 if (!request->startup_dpy.isEmpty()) 00571 { 00572 Display* dpy = NULL; 00573 if( (mCached_dpy != NULL) && 00574 (request->startup_dpy == XDisplayString( mCached_dpy ))) 00575 dpy = mCached_dpy; 00576 if( dpy == NULL ) 00577 dpy = XOpenDisplay(request->startup_dpy); 00578 if( dpy ) 00579 { 00580 KStartupInfoId id; 00581 id.initId(request->startup_id); 00582 KStartupInfo::sendFinishX( dpy, id ); 00583 if( mCached_dpy != dpy && mCached_dpy != NULL ) 00584 XCloseDisplay( mCached_dpy ); 00585 mCached_dpy = dpy; 00586 } 00587 } 00588 #endif 00589 } 00590 00591 if (request->autoStart) 00592 { 00593 mAutoTimer.start(0); 00594 } 00595 00596 if (request->transaction.type() != QDBusMessage::InvalidMessage) 00597 { 00598 if ( requestResult.dbusName.isNull() ) // null strings can't be sent 00599 requestResult.dbusName.clear(); 00600 Q_ASSERT( !requestResult.error.isNull() ); 00601 PIDType<sizeof(pid_t)>::PID_t stream_pid = requestResult.pid; 00602 QDBusConnection::sessionBus().send(request->transaction.createReply(QVariantList() << requestResult.result 00603 << requestResult.dbusName 00604 << requestResult.error 00605 << stream_pid)); 00606 } 00607 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00608 kDebug(7016) << "removing done request" << request->name << "PID" << request->pid; 00609 #endif 00610 00611 requestList.removeAll( request ); 00612 delete request; 00613 } 00614 00615 static void appendLong(QByteArray &ba, long l) 00616 { 00617 const int sz = ba.size(); 00618 ba.resize(sz + sizeof(long)); 00619 memcpy(ba.data() + sz, &l, sizeof(long)); 00620 } 00621 00622 void 00623 KLauncher::requestStart(KLaunchRequest *request) 00624 { 00625 #ifdef USE_KPROCESS_FOR_KIOSLAVES 00626 requestList.append( request ); 00627 lastRequest = request; 00628 00629 KProcess *process = new KProcess; 00630 process->setOutputChannelMode(KProcess::MergedChannels); 00631 connect(process ,SIGNAL(readyReadStandardOutput()),this, SLOT(slotGotOutput()) ); 00632 connect(process ,SIGNAL(finished(int, QProcess::ExitStatus)),this, SLOT(slotFinished(int, QProcess::ExitStatus)) ); 00633 request->process = process; 00634 00635 // process.setEnvironment(envlist); 00636 QStringList args; 00637 foreach (const QString &arg, request->arg_list) 00638 args << arg; 00639 00640 QString executable = request->name; 00641 #ifdef Q_WS_MAC 00642 const QString bundlepath = KStandardDirs::findExe(executable); 00643 if (!bundlepath.isEmpty()) 00644 executable = bundlepath; 00645 #endif 00646 process->setProgram(executable,args); 00647 process->start(); 00648 00649 if (!process->waitForStarted()) 00650 { 00651 processRequestReturn(LAUNCHER_ERROR,""); 00652 } 00653 else 00654 { 00655 request->pid = process->pid(); 00656 QByteArray data((char *)&request->pid, sizeof(int)); 00657 processRequestReturn(LAUNCHER_OK,data); 00658 } 00659 return; 00660 00661 #else 00662 requestList.append( request ); 00663 // Send request to kdeinit. 00664 klauncher_header request_header; 00665 QByteArray requestData; 00666 requestData.reserve(1024); 00667 00668 appendLong(requestData, request->arg_list.count() + 1); 00669 requestData.append(request->name.toLocal8Bit()); 00670 requestData.append('\0'); 00671 foreach (const QString &arg, request->arg_list) 00672 requestData.append(arg.toLocal8Bit()).append('\0'); 00673 appendLong(requestData, request->envs.count()); 00674 foreach (const QString &env, request->envs) 00675 requestData.append(env.toLocal8Bit()).append('\0'); 00676 appendLong(requestData, 0); // avoid_loops, always false here 00677 #ifdef Q_WS_X11 00678 bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0"; 00679 if( startup_notify ) 00680 requestData.append(request->startup_id).append('\0'); 00681 #endif 00682 if (!request->cwd.isEmpty()) 00683 requestData.append(QFile::encodeName(request->cwd)).append('\0'); 00684 00685 #ifdef Q_WS_X11 00686 request_header.cmd = startup_notify ? LAUNCHER_EXT_EXEC : LAUNCHER_EXEC_NEW; 00687 #else 00688 request_header.cmd = LAUNCHER_EXEC_NEW; 00689 #endif 00690 request_header.arg_length = requestData.length(); 00691 00692 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00693 kDebug(7016) << "Asking kdeinit to start" << request->name << request->arg_list 00694 << "cmd=" << commandToString(request_header.cmd); 00695 #endif 00696 00697 write(kdeinitSocket, &request_header, sizeof(request_header)); 00698 write(kdeinitSocket, requestData.data(), requestData.length()); 00699 00700 // Wait for pid to return. 00701 lastRequest = request; 00702 do { 00703 slotKDEInitData( kdeinitSocket ); 00704 } 00705 while (lastRequest != 0); 00706 #endif 00707 } 00708 00709 void KLauncher::exec_blind(const QString &name, const QStringList &arg_list, const QStringList &envs, const QString &startup_id) 00710 { 00711 KLaunchRequest *request = new KLaunchRequest; 00712 request->autoStart = false; 00713 request->name = name; 00714 request->arg_list = arg_list; 00715 request->dbus_startup_type = KService::DBusNone; 00716 request->pid = 0; 00717 request->status = KLaunchRequest::Launching; 00718 request->envs = envs; 00719 // Find service, if any - strip path if needed 00720 KService::Ptr service = KService::serviceByDesktopName( name.mid( name.lastIndexOf(QLatin1Char('/')) + 1 )); 00721 if (service) 00722 send_service_startup_info(request, service, startup_id.toLocal8Bit(), QStringList()); 00723 else // no .desktop file, no startup info 00724 cancel_service_startup_info( request, startup_id.toLocal8Bit(), envs ); 00725 00726 requestStart(request); 00727 // We don't care about this request any longer.... 00728 requestDone(request); 00729 } 00730 00731 00732 // KDE5: remove 00733 bool 00734 KLauncher::start_service_by_name(const QString &serviceName, const QStringList &urls, 00735 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) 00736 { 00737 KService::Ptr service; 00738 // Find service 00739 #ifndef KDE_NO_DEPRECATED 00740 service = KService::serviceByName(serviceName); 00741 #endif 00742 if (!service) 00743 { 00744 requestResult.result = ENOENT; 00745 requestResult.error = i18n("Could not find service '%1'.", serviceName); 00746 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any 00747 return false; 00748 } 00749 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); 00750 } 00751 00752 bool 00753 KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, 00754 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) 00755 { 00756 KService::Ptr service; 00757 // Find service 00758 const QFileInfo fi(serviceName); 00759 if (fi.isAbsolute() && fi.exists()) 00760 { 00761 // Full path 00762 service = new KService(serviceName); 00763 } 00764 else 00765 { 00766 service = KService::serviceByDesktopPath(serviceName); 00767 // TODO? 00768 //if (!service) 00769 // service = KService::serviceByStorageId(serviceName); // This method should be named start_service_by_storage_id ideally... 00770 } 00771 if (!service) 00772 { 00773 requestResult.result = ENOENT; 00774 requestResult.error = i18n("Could not find service '%1'.", serviceName); 00775 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any 00776 return false; 00777 } 00778 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); 00779 } 00780 00781 bool 00782 KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, 00783 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) 00784 { 00785 KService::Ptr service = KService::serviceByDesktopName(serviceName); 00786 if (!service) 00787 { 00788 requestResult.result = ENOENT; 00789 requestResult.error = i18n("Could not find service '%1'.", serviceName); 00790 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any 00791 return false; 00792 } 00793 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); 00794 } 00795 00796 bool 00797 KLauncher::start_service(KService::Ptr service, const QStringList &_urls, 00798 const QStringList &envs, const QByteArray &startup_id, 00799 bool blind, bool autoStart, const QDBusMessage &msg) 00800 { 00801 QStringList urls = _urls; 00802 bool runPermitted = KDesktopFile::isAuthorizedDesktopFile(service->entryPath()); 00803 00804 if (!service->isValid() || !runPermitted) 00805 { 00806 requestResult.result = ENOEXEC; 00807 if (service->isValid()) 00808 requestResult.error = i18n("Service '%1' must be executable to run.", service->entryPath()); 00809 else 00810 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath()); 00811 cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any 00812 return false; 00813 } 00814 KLaunchRequest *request = new KLaunchRequest; 00815 request->autoStart = autoStart; 00816 00817 if ((urls.count() > 1) && !service->allowMultipleFiles()) 00818 { 00819 // We need to launch the application N times. That sucks. 00820 // We ignore the result for application 2 to N. 00821 // For the first file we launch the application in the 00822 // usual way. The reported result is based on this 00823 // application. 00824 QStringList::ConstIterator it = urls.constBegin(); 00825 for(++it; 00826 it != urls.constEnd(); 00827 ++it) 00828 { 00829 QStringList singleUrl; 00830 singleUrl.append(*it); 00831 QByteArray startup_id2 = startup_id; 00832 if( !startup_id2.isEmpty() && startup_id2 != "0" ) 00833 startup_id2 = "0"; // can't use the same startup_id several times // krazy:exclude=doublequote_chars 00834 start_service( service, singleUrl, envs, startup_id2, true, false, msg); 00835 } 00836 QString firstURL = *(urls.begin()); 00837 urls.clear(); 00838 urls.append(firstURL); 00839 } 00840 createArgs(request, service, urls); 00841 00842 // We must have one argument at least! 00843 if (!request->arg_list.count()) 00844 { 00845 requestResult.result = ENOEXEC; 00846 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath()); 00847 delete request; 00848 cancel_service_startup_info( NULL, startup_id, envs ); 00849 return false; 00850 } 00851 00852 request->name = request->arg_list.takeFirst(); 00853 00854 if (request->name.endsWith(QLatin1String("/kioexec"))) { 00855 // Special case for kioexec; if createArgs said we were going to use it, 00856 // then we have to expect a kioexec-PID, not a org.kde.finalapp... 00857 // Testcase: konqueror www.kde.org, RMB on link, open with, kruler. 00858 00859 request->dbus_startup_type = KService::DBusMulti; 00860 request->dbus_name = QString::fromLatin1("org.kde.kioexec"); 00861 } else { 00862 request->dbus_startup_type = service->dbusStartupType(); 00863 00864 if ((request->dbus_startup_type == KService::DBusUnique) || 00865 (request->dbus_startup_type == KService::DBusMulti)) { 00866 const QVariant v = service->property(QLatin1String("X-DBUS-ServiceName")); 00867 if (v.isValid()) { 00868 request->dbus_name = v.toString(); 00869 } 00870 if (request->dbus_name.isEmpty()) { 00871 const QString binName = KRun::binaryName(service->exec(), true); 00872 request->dbus_name = QString::fromLatin1("org.kde.") + binName; 00873 request->tolerant_dbus_name = QString::fromLatin1("*.") + binName; 00874 } 00875 } 00876 } 00877 00878 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00879 kDebug(7016) << "name=" << request->name << "dbus_name=" << request->dbus_name 00880 << "startup type=" << s_DBusStartupTypeToString[request->dbus_startup_type]; 00881 #endif 00882 00883 request->pid = 0; 00884 request->envs = envs; 00885 send_service_startup_info( request, service, startup_id, envs ); 00886 00887 // Request will be handled later. 00888 if (!blind && !autoStart) 00889 { 00890 msg.setDelayedReply(true); 00891 request->transaction = msg; 00892 } 00893 queueRequest(request); 00894 return true; 00895 } 00896 00897 void 00898 KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QByteArray& startup_id, 00899 const QStringList &envs ) 00900 { 00901 #ifdef Q_WS_X11 00902 request->startup_id = "0";// krazy:exclude=doublequote_chars 00903 if (startup_id == "0") 00904 return; 00905 bool silent; 00906 QByteArray wmclass; 00907 if( !KRun::checkStartupNotify( QString(), service.data(), &silent, &wmclass )) 00908 return; 00909 KStartupInfoId id; 00910 id.initId(startup_id); 00911 QByteArray dpy_str; 00912 foreach (const QString &env, envs) { 00913 if (env.startsWith(QLatin1String("DISPLAY="))) 00914 dpy_str = env.mid(8).toLocal8Bit(); 00915 } 00916 Display* dpy = NULL; 00917 if (!dpy_str.isEmpty() && mCached_dpy != NULL && dpy_str != XDisplayString(mCached_dpy)) 00918 dpy = mCached_dpy; 00919 if (dpy == NULL) 00920 dpy = XOpenDisplay(dpy_str); 00921 request->startup_id = id.id(); 00922 if (dpy == NULL) { 00923 cancel_service_startup_info( request, startup_id, envs ); 00924 return; 00925 } 00926 00927 request->startup_dpy = dpy_str; 00928 00929 KStartupInfoData data; 00930 data.setName( service->name()); 00931 data.setIcon( service->icon()); 00932 data.setDescription( i18n( "Launching %1" , service->name())); 00933 if( !wmclass.isEmpty()) 00934 data.setWMClass( wmclass ); 00935 if( silent ) 00936 data.setSilent( KStartupInfoData::Yes ); 00937 data.setApplicationId( service->entryPath()); 00938 // the rest will be sent by kdeinit 00939 KStartupInfo::sendStartupX( dpy, id, data ); 00940 if( mCached_dpy != dpy && mCached_dpy != NULL ) 00941 XCloseDisplay( mCached_dpy ); 00942 mCached_dpy = dpy; 00943 return; 00944 #else 00945 return; 00946 #endif 00947 } 00948 00949 void 00950 KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QByteArray& startup_id, 00951 const QStringList &envs ) 00952 { 00953 #ifdef Q_WS_X11 00954 if( request != NULL ) 00955 request->startup_id = "0"; // krazy:exclude=doublequote_chars 00956 if( !startup_id.isEmpty() && startup_id != "0" ) 00957 { 00958 QString dpy_str; 00959 foreach (const QString &env, envs) { 00960 if (env.startsWith(QLatin1String("DISPLAY="))) 00961 dpy_str = env.mid(8); 00962 } 00963 Display* dpy = NULL; 00964 if( !dpy_str.isEmpty() && mCached_dpy != NULL 00965 && dpy_str != QLatin1String(XDisplayString( mCached_dpy )) ) 00966 dpy = mCached_dpy; 00967 if( dpy == NULL ) 00968 dpy = XOpenDisplay( dpy_str.toLatin1().constData() ); 00969 if( dpy == NULL ) 00970 return; 00971 KStartupInfoId id; 00972 id.initId(startup_id); 00973 KStartupInfo::sendFinishX( dpy, id ); 00974 if( mCached_dpy != dpy && mCached_dpy != NULL ) 00975 XCloseDisplay( mCached_dpy ); 00976 mCached_dpy = dpy; 00977 } 00978 #endif 00979 } 00980 00981 bool 00982 KLauncher::kdeinit_exec(const QString &app, const QStringList &args, 00983 const QString& workdir, const QStringList &envs, 00984 const QString &startup_id, bool wait, const QDBusMessage &msg) 00985 { 00986 KLaunchRequest *request = new KLaunchRequest; 00987 request->autoStart = false; 00988 request->arg_list = args; 00989 request->name = app; 00990 if (wait) 00991 request->dbus_startup_type = KService::DBusWait; 00992 else 00993 request->dbus_startup_type = KService::DBusNone; 00994 request->pid = 0; 00995 #ifdef Q_WS_X11 00996 request->startup_id = startup_id.toLocal8Bit(); 00997 #endif 00998 request->envs = envs; 00999 request->cwd = workdir; 01000 #ifdef Q_WS_X11 01001 if (!app.endsWith(QLatin1String("kbuildsycoca4"))) { // avoid stupid loop 01002 // Find service, if any - strip path if needed 01003 const QString desktopName = app.mid(app.lastIndexOf(QLatin1Char('/')) + 1); 01004 KService::Ptr service = KService::serviceByDesktopName(desktopName); 01005 if (service) 01006 send_service_startup_info(request, service, 01007 request->startup_id, envs); 01008 else // no .desktop file, no startup info 01009 cancel_service_startup_info(request, request->startup_id, envs); 01010 } 01011 #endif 01012 msg.setDelayedReply(true); 01013 request->transaction = msg; 01014 queueRequest(request); 01015 return true; 01016 } 01017 01018 void 01019 KLauncher::queueRequest(KLaunchRequest *request) 01020 { 01021 requestQueue.append( request ); 01022 if (!bProcessingQueue) 01023 { 01024 bProcessingQueue = true; 01025 QTimer::singleShot(0, this, SLOT( slotDequeue() )); 01026 } 01027 } 01028 01029 void 01030 KLauncher::slotDequeue() 01031 { 01032 do { 01033 KLaunchRequest *request = requestQueue.takeFirst(); 01034 // process request 01035 request->status = KLaunchRequest::Launching; 01036 requestStart(request); 01037 if (request->status != KLaunchRequest::Launching) 01038 { 01039 // Request handled. 01040 #ifdef KLAUNCHER_VERBOSE_OUTPUT 01041 kDebug(7016) << "Request handled already"; 01042 #endif 01043 requestDone( request ); 01044 continue; 01045 } 01046 } while(requestQueue.count()); 01047 bProcessingQueue = false; 01048 } 01049 01050 void 01051 KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service , 01052 const QStringList &urls) 01053 { 01054 const QStringList params = KRun::processDesktopExec(*service, urls); 01055 01056 for(QStringList::ConstIterator it = params.begin(); 01057 it != params.end(); ++it) 01058 { 01059 request->arg_list.append(*it); 01060 } 01061 request->cwd = service->path(); 01062 } 01063 01065 01066 pid_t 01067 KLauncher::requestHoldSlave(const KUrl &url, const QString &app_socket) 01068 { 01069 IdleSlave *slave = 0; 01070 foreach (IdleSlave *p, mSlaveList) 01071 { 01072 if (p->onHold(url)) 01073 { 01074 slave = p; 01075 break; 01076 } 01077 } 01078 if (slave) 01079 { 01080 mSlaveList.removeAll(slave); 01081 slave->connect(app_socket); 01082 return slave->pid(); 01083 } 01084 return 0; 01085 } 01086 01087 pid_t 01088 KLauncher::requestSlave(const QString &protocol, 01089 const QString &host, 01090 const QString &app_socket, 01091 QString &error) 01092 { 01093 IdleSlave *slave = 0; 01094 foreach (IdleSlave *p, mSlaveList) 01095 { 01096 if (p->match(protocol, host, true)) 01097 { 01098 slave = p; 01099 break; 01100 } 01101 } 01102 if (!slave) 01103 { 01104 foreach (IdleSlave *p, mSlaveList) 01105 { 01106 if (p->match(protocol, host, false)) 01107 { 01108 slave = p; 01109 break; 01110 } 01111 } 01112 } 01113 if (!slave) 01114 { 01115 foreach (IdleSlave *p, mSlaveList) 01116 { 01117 if (p->match(protocol, QString(), false)) 01118 { 01119 slave = p; 01120 break; 01121 } 01122 } 01123 } 01124 if (slave) 01125 { 01126 mSlaveList.removeAll(slave); 01127 slave->connect(app_socket); 01128 return slave->pid(); 01129 } 01130 01131 QString name = KProtocolInfo::exec(protocol); 01132 if (name.isEmpty()) 01133 { 01134 error = i18n("Unknown protocol '%1'.\n", protocol); 01135 return 0; 01136 } 01137 01138 QStringList arg_list; 01139 #ifdef USE_KPROCESS_FOR_KIOSLAVES 01140 arg_list << name; 01141 arg_list << protocol; 01142 arg_list << mConnectionServer.address(); 01143 arg_list << app_socket; 01144 name = KStandardDirs::findExe(QLatin1String("kioslave")); 01145 #else 01146 QString arg1 = protocol; 01147 QString arg2 = mConnectionServer.address(); 01148 QString arg3 = app_socket; 01149 arg_list.append(arg1); 01150 arg_list.append(arg2); 01151 arg_list.append(arg3); 01152 #endif 01153 01154 kDebug(7016) << "KLauncher: launching new slave " << name << " with protocol=" << protocol 01155 << " args=" << arg_list << endl; 01156 01157 #ifdef Q_OS_UNIX 01158 if (mSlaveDebug == protocol) 01159 { 01160 #ifndef USE_KPROCESS_FOR_KIOSLAVES 01161 klauncher_header request_header; 01162 request_header.cmd = LAUNCHER_DEBUG_WAIT; 01163 request_header.arg_length = 0; 01164 write(kdeinitSocket, &request_header, sizeof(request_header)); 01165 #else 01166 name = QString::fromLatin1("gdb"); 01167 #endif 01168 } 01169 if (mSlaveValgrind == protocol) { 01170 #ifndef USE_KPROCESS_FOR_KIOSLAVES // otherwise we've already done this 01171 KLibrary lib(name, KGlobal::mainComponent()); 01172 arg_list.prepend(lib.fileName()); 01173 arg_list.prepend(KStandardDirs::locate("exe", QString::fromLatin1("kioslave"))); 01174 #endif 01175 name = QString::fromLatin1("valgrind"); 01176 01177 if (!mSlaveValgrindSkin.isEmpty()) { 01178 arg_list.prepend(QLatin1String("--tool=") + mSlaveValgrindSkin); 01179 } else 01180 arg_list.prepend(QLatin1String("--tool=memcheck")); 01181 } 01182 #endif 01183 KLaunchRequest *request = new KLaunchRequest; 01184 request->autoStart = false; 01185 request->name = name; 01186 request->arg_list = arg_list; 01187 request->dbus_startup_type = KService::DBusNone; 01188 request->pid = 0; 01189 #ifdef Q_WS_X11 01190 request->startup_id = "0"; // krazy:exclude=doublequote_chars 01191 #endif 01192 request->status = KLaunchRequest::Launching; 01193 requestStart(request); 01194 pid_t pid = request->pid; 01195 01196 // kDebug(7016) << "Slave launched, pid = " << pid; 01197 01198 // We don't care about this request any longer.... 01199 requestDone(request); 01200 if (!pid) 01201 { 01202 error = i18n("Error loading '%1'.\n", name); 01203 } 01204 return pid; 01205 } 01206 01207 void 01208 KLauncher::waitForSlave(int pid, const QDBusMessage &msg) 01209 { 01210 foreach (IdleSlave *slave, mSlaveList) 01211 { 01212 if (slave->pid() == static_cast<pid_t>(pid)) 01213 return; // Already here. 01214 } 01215 SlaveWaitRequest *waitRequest = new SlaveWaitRequest; 01216 msg.setDelayedReply(true); 01217 waitRequest->transaction = msg; 01218 waitRequest->pid = static_cast<pid_t>(pid); 01219 mSlaveWaitRequest.append(waitRequest); 01220 } 01221 01222 void 01223 KLauncher::acceptSlave() 01224 { 01225 IdleSlave *slave = new IdleSlave(this); 01226 mConnectionServer.setNextPendingConnection(&slave->mConn); 01227 mSlaveList.append(slave); 01228 connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone())); 01229 connect(slave, SIGNAL(statusUpdate(IdleSlave *)), 01230 this, SLOT(slotSlaveStatus(IdleSlave *))); 01231 if (!mTimer.isActive()) 01232 { 01233 mTimer.start(1000*10); 01234 } 01235 } 01236 01237 void 01238 KLauncher::slotSlaveStatus(IdleSlave *slave) 01239 { 01240 QMutableListIterator<SlaveWaitRequest *> it(mSlaveWaitRequest); 01241 while(it.hasNext()) 01242 { 01243 SlaveWaitRequest *waitRequest = it.next(); 01244 if (waitRequest->pid == slave->pid()) 01245 { 01246 QDBusConnection::sessionBus().send(waitRequest->transaction.createReply()); 01247 it.remove(); 01248 delete waitRequest; 01249 } 01250 } 01251 } 01252 01253 void 01254 KLauncher::slotSlaveGone() 01255 { 01256 IdleSlave *slave = (IdleSlave *) sender(); 01257 mSlaveList.removeAll(slave); 01258 if ((mSlaveList.count() == 0) && (mTimer.isActive())) 01259 { 01260 mTimer.stop(); 01261 } 01262 } 01263 01264 void 01265 KLauncher::idleTimeout() 01266 { 01267 bool keepOneFileSlave=true; 01268 time_t now = time(0); 01269 foreach (IdleSlave *slave, mSlaveList) 01270 { 01271 if ((slave->protocol()==QLatin1String("file")) && (keepOneFileSlave)) 01272 keepOneFileSlave=false; 01273 else if (slave->age(now) > SLAVE_MAX_IDLE) 01274 { 01275 // killing idle slave 01276 delete slave; 01277 } 01278 } 01279 } 01280 01281 void KLauncher::reparseConfiguration() 01282 { 01283 KProtocolManager::reparseConfiguration(); 01284 foreach (IdleSlave *slave, mSlaveList) 01285 slave->reparseConfiguration(); 01286 } 01287 01288 01289 void 01290 KLauncher::slotGotOutput() 01291 { 01292 #ifdef USE_KPROCESS_FOR_KIOSLAVES 01293 KProcess *p = static_cast<KProcess *>(sender()); 01294 QByteArray _stdout = p->readAllStandardOutput(); 01295 kDebug(7016) << _stdout.data(); 01296 #endif 01297 } 01298 01299 void 01300 KLauncher::slotFinished(int exitCode, QProcess::ExitStatus exitStatus ) 01301 { 01302 #ifdef USE_KPROCESS_FOR_KIOSLAVES 01303 KProcess *p = static_cast<KProcess *>(sender()); 01304 kDebug(7016) << "process finished exitcode=" << exitCode << "exitStatus=" << exitStatus; 01305 01306 foreach (KLaunchRequest *request, requestList) 01307 { 01308 if (request->process == p) 01309 { 01310 #ifdef KLAUNCHER_VERBOSE_OUTPUT 01311 kDebug(7016) << "found KProcess, request done"; 01312 #endif 01313 if (exitCode == 0 && exitStatus == QProcess::NormalExit) 01314 request->status = KLaunchRequest::Done; 01315 else 01316 request->status = KLaunchRequest::Error; 01317 requestDone(request); 01318 request->process = 0; 01319 } 01320 } 01321 delete p; 01322 #else 01323 Q_UNUSED(exitCode); 01324 Q_UNUSED(exitStatus); 01325 #endif 01326 } 01327 01328 01329 void KLauncher::terminate_kdeinit() 01330 { 01331 kDebug(7016); 01332 #ifndef USE_KPROCESS_FOR_KIOSLAVES 01333 klauncher_header request_header; 01334 request_header.cmd = LAUNCHER_TERMINATE_KDEINIT; 01335 request_header.arg_length = 0; 01336 write(kdeinitSocket, &request_header, sizeof(request_header)); 01337 #endif 01338 } 01339 01340 #include "klauncher.moc"
KDE 4.6 API Reference