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 Q_WS_WIN 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 Q_WS_WIN 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 Q_WS_WIN 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 #ifdef Q_WS_WIN 00268 00269 #else 00270 klauncher_header request_header; 00271 QByteArray requestData; 00272 requestData.append(name.toLocal8Bit()).append('\0').append(value.toLocal8Bit()).append('\0'); 00273 request_header.cmd = LAUNCHER_SETENV; 00274 request_header.arg_length = requestData.size(); 00275 write(kdeinitSocket, &request_header, sizeof(request_header)); 00276 write(kdeinitSocket, requestData.data(), request_header.arg_length); 00277 #endif 00278 } 00279 00280 #ifndef Q_WS_WIN 00281 /* 00282 * Read 'len' bytes from 'sock' into buffer. 00283 * returns -1 on failure, 0 on no data. 00284 */ 00285 static int 00286 read_socket(int sock, char *buffer, int len) 00287 { 00288 ssize_t result; 00289 int bytes_left = len; 00290 while (bytes_left > 0) { 00291 // in case we get a request to start an application and data arrive 00292 // to kdeinitSocket at the same time, requestStart() will already 00293 // call slotKDEInitData(), so we must check there's still something 00294 // to read, otherwise this would block 00295 00296 // Same thing if kdeinit dies without warning. 00297 00298 fd_set in; 00299 timeval tm = { 30, 0 }; // 30 seconds timeout, so we're not stuck in case kdeinit dies on us 00300 FD_ZERO ( &in ); 00301 FD_SET( sock, &in ); 00302 select( sock + 1, &in, 0, 0, &tm ); 00303 if( !FD_ISSET( sock, &in )) { 00304 kDebug(7016) << "read_socket" << sock << "nothing to read, kdeinit4 must be dead"; 00305 return -1; 00306 } 00307 00308 result = read(sock, buffer, bytes_left); 00309 if (result > 0) 00310 { 00311 buffer += result; 00312 bytes_left -= result; 00313 } 00314 else if (result == 0) 00315 return -1; 00316 else if ((result == -1) && (errno != EINTR)) 00317 return -1; 00318 } 00319 return 0; 00320 } 00321 00322 00323 void 00324 KLauncher::slotKDEInitData(int) 00325 { 00326 klauncher_header request_header; 00327 QByteArray requestData; 00328 00329 int result = read_socket(kdeinitSocket, (char *) &request_header, 00330 sizeof( request_header)); 00331 if (result == -1) 00332 { 00333 kDebug(7016) << "Exiting on read_socket errno:" << errno; 00334 KDE_signal( SIGHUP, SIG_IGN); 00335 KDE_signal( SIGTERM, SIG_IGN); 00336 destruct(); // Exit! 00337 } 00338 requestData.resize(request_header.arg_length); 00339 result = read_socket(kdeinitSocket, (char *) requestData.data(), 00340 request_header.arg_length); 00341 00342 processRequestReturn(request_header.cmd,requestData); 00343 00344 } 00345 #endif 00346 00347 void KLauncher::processRequestReturn(int status, const QByteArray &requestData) 00348 { 00349 if (status == LAUNCHER_CHILD_DIED) 00350 { 00351 long *request_data; 00352 request_data = (long *) requestData.data(); 00353 processDied(request_data[0], request_data[1]); 00354 return; 00355 } 00356 if (lastRequest && (status == LAUNCHER_OK)) 00357 { 00358 long *request_data; 00359 request_data = (long *) requestData.data(); 00360 lastRequest->pid = (pid_t) (*request_data); 00361 kDebug(7016).nospace() << lastRequest->name << " (pid " << lastRequest->pid << 00362 ") up and running."; 00363 switch(lastRequest->dbus_startup_type) 00364 { 00365 case KService::DBusNone: 00366 lastRequest->status = KLaunchRequest::Running; 00367 break; 00368 case KService::DBusUnique: 00369 case KService::DBusWait: 00370 case KService::DBusMulti: 00371 lastRequest->status = KLaunchRequest::Launching; 00372 break; 00373 } 00374 lastRequest = 0; 00375 return; 00376 } 00377 if (lastRequest && (status == LAUNCHER_ERROR)) 00378 { 00379 lastRequest->status = KLaunchRequest::Error; 00380 kDebug(7016) << lastRequest->name << " failed." << endl; 00381 if (!requestData.isEmpty()) 00382 lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data()); 00383 lastRequest = 0; 00384 return; 00385 } 00386 00387 kWarning(7016)<< "Unexpected request return" << (unsigned int) status; 00388 } 00389 00390 void 00391 KLauncher::processDied(pid_t pid, long exitStatus) 00392 { 00393 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00394 kDebug(7016) << pid << "exitStatus=" << exitStatus; 00395 #else 00396 Q_UNUSED(exitStatus); 00397 // We should probably check the exitStatus for the uniqueapp case? 00398 #endif 00399 foreach (KLaunchRequest *request, requestList) 00400 { 00401 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00402 kDebug(7016) << " had pending request" << request->pid; 00403 #endif 00404 if (request->pid == pid) 00405 { 00406 if (request->dbus_startup_type == KService::DBusWait) 00407 request->status = KLaunchRequest::Done; 00408 else if ((request->dbus_startup_type == KService::DBusUnique) 00409 && QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { 00410 request->status = KLaunchRequest::Running; 00411 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00412 kDebug(7016) << pid << "running as a unique app"; 00413 #endif 00414 } else { 00415 request->status = KLaunchRequest::Error; 00416 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00417 kDebug(7016) << pid << "died, requestDone. status=" << request->status; 00418 #endif 00419 } 00420 requestDone(request); 00421 return; 00422 } 00423 } 00424 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00425 kDebug(7016) << "found no pending requests for PID" << pid; 00426 #endif 00427 } 00428 00429 static bool matchesPendingRequest(const QString& appId, const QString& pendingAppId) 00430 { 00431 // appId just registered, e.g. org.koffice.kword-12345 00432 // Let's see if this is what pendingAppId (e.g. org.koffice.kword or *.kword) was waiting for. 00433 00434 const QString newAppId = appId.left(appId.lastIndexOf(QLatin1Char('-'))); // strip out the -12345 if present. 00435 00436 //kDebug() << "appId=" << appId << "newAppId=" << newAppId << "pendingAppId=" << pendingAppId; 00437 00438 if (pendingAppId.startsWith(QLatin1String("*."))) { 00439 const QString pendingName = pendingAppId.mid(2); 00440 const QString appName = newAppId.mid(newAppId.lastIndexOf(QLatin1Char('.'))+1); 00441 //kDebug() << "appName=" << appName; 00442 return appName == pendingName; 00443 } 00444 00445 return newAppId == pendingAppId; 00446 } 00447 00448 void 00449 KLauncher::slotNameOwnerChanged(const QString &appId, const QString &oldOwner, 00450 const QString &newOwner) 00451 { 00452 Q_UNUSED(oldOwner); 00453 if (appId.isEmpty() || newOwner.isEmpty()) 00454 return; 00455 00456 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00457 kDebug(7016) << "new app" << appId; 00458 #endif 00459 foreach (KLaunchRequest *request, requestList) 00460 { 00461 if (request->status != KLaunchRequest::Launching) 00462 continue; 00463 00464 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00465 kDebug(7016) << "had pending request" << request->name << s_DBusStartupTypeToString[request->dbus_startup_type] << "dbus_name" << request->dbus_name << request->tolerant_dbus_name; 00466 #endif 00467 // For unique services check the requested service name first 00468 if (request->dbus_startup_type == KService::DBusUnique) { 00469 if ((appId == request->dbus_name) || // just started 00470 QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { // was already running 00471 request->status = KLaunchRequest::Running; 00472 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00473 kDebug(7016) << "OK, unique app" << request->dbus_name << "is running"; 00474 #endif 00475 requestDone(request); 00476 continue; 00477 } else { 00478 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00479 kDebug(7016) << "unique app" << request->dbus_name << "not running yet"; 00480 #endif 00481 } 00482 } 00483 00484 const QString rAppId = !request->tolerant_dbus_name.isEmpty() ? request->tolerant_dbus_name : request->dbus_name; 00485 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00486 //kDebug(7016) << "using" << rAppId << "for matching"; 00487 #endif 00488 if (rAppId.isEmpty()) 00489 continue; 00490 00491 if (matchesPendingRequest(appId, rAppId)) { 00492 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00493 kDebug(7016) << "ok, request done"; 00494 #endif 00495 request->dbus_name = appId; 00496 request->status = KLaunchRequest::Running; 00497 requestDone(request); 00498 continue; 00499 } 00500 } 00501 } 00502 00503 void 00504 KLauncher::autoStart(int phase) 00505 { 00506 if( mAutoStart.phase() >= phase ) 00507 return; 00508 mAutoStart.setPhase(phase); 00509 if (phase == 0) 00510 mAutoStart.loadAutoStartList(); 00511 mAutoTimer.start(0); 00512 } 00513 00514 void 00515 KLauncher::slotAutoStart() 00516 { 00517 KService::Ptr s; 00518 do 00519 { 00520 QString service = mAutoStart.startService(); 00521 if (service.isEmpty()) 00522 { 00523 // Done 00524 if( !mAutoStart.phaseDone()) 00525 { 00526 mAutoStart.setPhaseDone(); 00527 switch( mAutoStart.phase()) 00528 { 00529 case 0: 00530 emit autoStart0Done(); 00531 break; 00532 case 1: 00533 emit autoStart1Done(); 00534 break; 00535 case 2: 00536 emit autoStart2Done(); 00537 break; 00538 } 00539 } 00540 return; 00541 } 00542 s = new KService(service); 00543 } 00544 while (!start_service(s, QStringList(), QStringList(), "0", false, true, QDBusMessage())); 00545 // Loop till we find a service that we can start. 00546 } 00547 00548 void 00549 KLauncher::requestDone(KLaunchRequest *request) 00550 { 00551 if ((request->status == KLaunchRequest::Running) || 00552 (request->status == KLaunchRequest::Done)) 00553 { 00554 requestResult.result = 0; 00555 requestResult.dbusName = request->dbus_name; 00556 requestResult.error = QString::fromLatin1(""); // not null, cf assert further down 00557 requestResult.pid = request->pid; 00558 } 00559 else 00560 { 00561 requestResult.result = 1; 00562 requestResult.dbusName.clear(); 00563 requestResult.error = i18n("KDEInit could not launch '%1'.", request->name); 00564 if (!request->errorMsg.isEmpty()) 00565 requestResult.error += QString::fromLatin1(":\n") + request->errorMsg; 00566 requestResult.pid = 0; 00567 00568 #ifdef Q_WS_X11 00569 if (!request->startup_dpy.isEmpty()) 00570 { 00571 Display* dpy = NULL; 00572 if( (mCached_dpy != NULL) && 00573 (request->startup_dpy == XDisplayString( mCached_dpy ))) 00574 dpy = mCached_dpy; 00575 if( dpy == NULL ) 00576 dpy = XOpenDisplay(request->startup_dpy); 00577 if( dpy ) 00578 { 00579 KStartupInfoId id; 00580 id.initId(request->startup_id); 00581 KStartupInfo::sendFinishX( dpy, id ); 00582 if( mCached_dpy != dpy && mCached_dpy != NULL ) 00583 XCloseDisplay( mCached_dpy ); 00584 mCached_dpy = dpy; 00585 } 00586 } 00587 #endif 00588 } 00589 00590 if (request->autoStart) 00591 { 00592 mAutoTimer.start(0); 00593 } 00594 00595 if (request->transaction.type() != QDBusMessage::InvalidMessage) 00596 { 00597 if ( requestResult.dbusName.isNull() ) // null strings can't be sent 00598 requestResult.dbusName.clear(); 00599 Q_ASSERT( !requestResult.error.isNull() ); 00600 PIDType<sizeof(pid_t)>::PID_t stream_pid = requestResult.pid; 00601 QDBusConnection::sessionBus().send(request->transaction.createReply(QVariantList() << requestResult.result 00602 << requestResult.dbusName 00603 << requestResult.error 00604 << stream_pid)); 00605 } 00606 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00607 kDebug(7016) << "removing done request" << request->name << "PID" << request->pid; 00608 #endif 00609 00610 requestList.removeAll( request ); 00611 delete request; 00612 } 00613 00614 static void appendLong(QByteArray &ba, long l) 00615 { 00616 const int sz = ba.size(); 00617 ba.resize(sz + sizeof(long)); 00618 memcpy(ba.data() + sz, &l, sizeof(long)); 00619 } 00620 00621 void 00622 KLauncher::requestStart(KLaunchRequest *request) 00623 { 00624 #ifdef Q_WS_WIN 00625 requestList.append( request ); 00626 lastRequest = request; 00627 00628 KProcess *process = new KProcess; 00629 process->setOutputChannelMode(KProcess::MergedChannels); 00630 connect(process ,SIGNAL(readyReadStandardOutput()),this, SLOT(slotGotOutput()) ); 00631 connect(process ,SIGNAL(finished(int, QProcess::ExitStatus)),this, SLOT(slotFinished(int, QProcess::ExitStatus)) ); 00632 request->process = process; 00633 00634 // process.setEnvironment(envlist); 00635 QStringList args; 00636 foreach (const QString &arg, request->arg_list) 00637 args << arg; 00638 00639 process->setProgram(request->name,args); 00640 process->start(); 00641 00642 if (!process->waitForStarted()) 00643 { 00644 processRequestReturn(LAUNCHER_ERROR,""); 00645 } 00646 else 00647 { 00648 request->pid = process->pid(); 00649 QByteArray data((char *)&request->pid, sizeof(int)); 00650 processRequestReturn(LAUNCHER_OK,data); 00651 } 00652 return; 00653 00654 #else 00655 requestList.append( request ); 00656 // Send request to kdeinit. 00657 klauncher_header request_header; 00658 QByteArray requestData; 00659 requestData.reserve(1024); 00660 00661 appendLong(requestData, request->arg_list.count() + 1); 00662 requestData.append(request->name.toLocal8Bit()); 00663 requestData.append('\0'); 00664 foreach (const QString &arg, request->arg_list) 00665 requestData.append(arg.toLocal8Bit()).append('\0'); 00666 appendLong(requestData, request->envs.count()); 00667 foreach (const QString &env, request->envs) 00668 requestData.append(env.toLocal8Bit()).append('\0'); 00669 appendLong(requestData, 0); // avoid_loops, always false here 00670 #ifdef Q_WS_X11 00671 bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0"; 00672 if( startup_notify ) 00673 requestData.append(request->startup_id).append('\0'); 00674 #endif 00675 if (!request->cwd.isEmpty()) 00676 requestData.append(QFile::encodeName(request->cwd)).append('\0'); 00677 00678 #ifdef Q_WS_X11 00679 request_header.cmd = startup_notify ? LAUNCHER_EXT_EXEC : LAUNCHER_EXEC_NEW; 00680 #else 00681 request_header.cmd = LAUNCHER_EXEC_NEW; 00682 #endif 00683 request_header.arg_length = requestData.length(); 00684 00685 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00686 kDebug(7016) << "Asking kdeinit to start" << request->name << request->arg_list 00687 << "cmd=" << commandToString(request_header.cmd); 00688 #endif 00689 00690 write(kdeinitSocket, &request_header, sizeof(request_header)); 00691 write(kdeinitSocket, requestData.data(), requestData.length()); 00692 00693 // Wait for pid to return. 00694 lastRequest = request; 00695 do { 00696 slotKDEInitData( kdeinitSocket ); 00697 } 00698 while (lastRequest != 0); 00699 #endif 00700 } 00701 00702 void KLauncher::exec_blind(const QString &name, const QStringList &arg_list, const QStringList &envs, const QString &startup_id) 00703 { 00704 KLaunchRequest *request = new KLaunchRequest; 00705 request->autoStart = false; 00706 request->name = name; 00707 request->arg_list = arg_list; 00708 request->dbus_startup_type = KService::DBusNone; 00709 request->pid = 0; 00710 request->status = KLaunchRequest::Launching; 00711 request->envs = envs; 00712 // Find service, if any - strip path if needed 00713 KService::Ptr service = KService::serviceByDesktopName( name.mid( name.lastIndexOf(QLatin1Char('/')) + 1 )); 00714 if (service) 00715 send_service_startup_info(request, service, startup_id.toLocal8Bit(), QStringList()); 00716 else // no .desktop file, no startup info 00717 cancel_service_startup_info( request, startup_id.toLocal8Bit(), envs ); 00718 00719 requestStart(request); 00720 // We don't care about this request any longer.... 00721 requestDone(request); 00722 } 00723 00724 00725 // KDE5: remove 00726 bool 00727 KLauncher::start_service_by_name(const QString &serviceName, const QStringList &urls, 00728 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) 00729 { 00730 KService::Ptr service; 00731 // Find service 00732 #ifndef KDE_NO_DEPRECATED 00733 service = KService::serviceByName(serviceName); 00734 #endif 00735 if (!service) 00736 { 00737 requestResult.result = ENOENT; 00738 requestResult.error = i18n("Could not find service '%1'.", serviceName); 00739 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any 00740 return false; 00741 } 00742 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); 00743 } 00744 00745 bool 00746 KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, 00747 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) 00748 { 00749 KService::Ptr service; 00750 // Find service 00751 const QFileInfo fi(serviceName); 00752 if (fi.isAbsolute() && fi.exists()) 00753 { 00754 // Full path 00755 service = new KService(serviceName); 00756 } 00757 else 00758 { 00759 service = KService::serviceByDesktopPath(serviceName); 00760 // TODO? 00761 //if (!service) 00762 // service = KService::serviceByStorageId(serviceName); // This method should be named start_service_by_storage_id ideally... 00763 } 00764 if (!service) 00765 { 00766 requestResult.result = ENOENT; 00767 requestResult.error = i18n("Could not find service '%1'.", serviceName); 00768 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any 00769 return false; 00770 } 00771 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); 00772 } 00773 00774 bool 00775 KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, 00776 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) 00777 { 00778 KService::Ptr service = KService::serviceByDesktopName(serviceName); 00779 if (!service) 00780 { 00781 requestResult.result = ENOENT; 00782 requestResult.error = i18n("Could not find service '%1'.", serviceName); 00783 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any 00784 return false; 00785 } 00786 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); 00787 } 00788 00789 bool 00790 KLauncher::start_service(KService::Ptr service, const QStringList &_urls, 00791 const QStringList &envs, const QByteArray &startup_id, 00792 bool blind, bool autoStart, const QDBusMessage &msg) 00793 { 00794 QStringList urls = _urls; 00795 bool runPermitted = KDesktopFile::isAuthorizedDesktopFile(service->entryPath()); 00796 00797 if (!service->isValid() || !runPermitted) 00798 { 00799 requestResult.result = ENOEXEC; 00800 if (service->isValid()) 00801 requestResult.error = i18n("Service '%1' must be executable to run.", service->entryPath()); 00802 else 00803 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath()); 00804 cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any 00805 return false; 00806 } 00807 KLaunchRequest *request = new KLaunchRequest; 00808 request->autoStart = autoStart; 00809 00810 if ((urls.count() > 1) && !service->allowMultipleFiles()) 00811 { 00812 // We need to launch the application N times. That sucks. 00813 // We ignore the result for application 2 to N. 00814 // For the first file we launch the application in the 00815 // usual way. The reported result is based on this 00816 // application. 00817 QStringList::ConstIterator it = urls.constBegin(); 00818 for(++it; 00819 it != urls.constEnd(); 00820 ++it) 00821 { 00822 QStringList singleUrl; 00823 singleUrl.append(*it); 00824 QByteArray startup_id2 = startup_id; 00825 if( !startup_id2.isEmpty() && startup_id2 != "0" ) 00826 startup_id2 = "0"; // can't use the same startup_id several times // krazy:exclude=doublequote_chars 00827 start_service( service, singleUrl, envs, startup_id2, true, false, msg); 00828 } 00829 QString firstURL = *(urls.begin()); 00830 urls.clear(); 00831 urls.append(firstURL); 00832 } 00833 createArgs(request, service, urls); 00834 00835 // We must have one argument at least! 00836 if (!request->arg_list.count()) 00837 { 00838 requestResult.result = ENOEXEC; 00839 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath()); 00840 delete request; 00841 cancel_service_startup_info( NULL, startup_id, envs ); 00842 return false; 00843 } 00844 00845 request->name = request->arg_list.takeFirst(); 00846 00847 if (request->name.endsWith(QLatin1String("/kioexec"))) { 00848 // Special case for kioexec; if createArgs said we were going to use it, 00849 // then we have to expect a kioexec-PID, not a org.kde.finalapp... 00850 // Testcase: konqueror www.kde.org, RMB on link, open with, kruler. 00851 00852 request->dbus_startup_type = KService::DBusMulti; 00853 request->dbus_name = QString::fromLatin1("org.kde.kioexec"); 00854 } else { 00855 request->dbus_startup_type = service->dbusStartupType(); 00856 00857 if ((request->dbus_startup_type == KService::DBusUnique) || 00858 (request->dbus_startup_type == KService::DBusMulti)) { 00859 const QVariant v = service->property(QLatin1String("X-DBUS-ServiceName")); 00860 if (v.isValid()) { 00861 request->dbus_name = v.toString(); 00862 } 00863 if (request->dbus_name.isEmpty()) { 00864 const QString binName = KRun::binaryName(service->exec(), true); 00865 request->dbus_name = QString::fromLatin1("org.kde.") + binName; 00866 request->tolerant_dbus_name = QString::fromLatin1("*.") + binName; 00867 } 00868 } 00869 } 00870 00871 #ifdef KLAUNCHER_VERBOSE_OUTPUT 00872 kDebug(7016) << "name=" << request->name << "dbus_name=" << request->dbus_name 00873 << "startup type=" << s_DBusStartupTypeToString[request->dbus_startup_type]; 00874 #endif 00875 00876 request->pid = 0; 00877 request->envs = envs; 00878 send_service_startup_info( request, service, startup_id, envs ); 00879 00880 // Request will be handled later. 00881 if (!blind && !autoStart) 00882 { 00883 msg.setDelayedReply(true); 00884 request->transaction = msg; 00885 } 00886 queueRequest(request); 00887 return true; 00888 } 00889 00890 void 00891 KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QByteArray& startup_id, 00892 const QStringList &envs ) 00893 { 00894 #ifdef Q_WS_X11 00895 request->startup_id = "0";// krazy:exclude=doublequote_chars 00896 if (startup_id == "0") 00897 return; 00898 bool silent; 00899 QByteArray wmclass; 00900 if( !KRun::checkStartupNotify( QString(), service.data(), &silent, &wmclass )) 00901 return; 00902 KStartupInfoId id; 00903 id.initId(startup_id); 00904 QByteArray dpy_str; 00905 foreach (const QString &env, envs) { 00906 if (env.startsWith(QLatin1String("DISPLAY="))) 00907 dpy_str = env.mid(8).toLocal8Bit(); 00908 } 00909 Display* dpy = NULL; 00910 if (!dpy_str.isEmpty() && mCached_dpy != NULL && dpy_str != XDisplayString(mCached_dpy)) 00911 dpy = mCached_dpy; 00912 if (dpy == NULL) 00913 dpy = XOpenDisplay(dpy_str); 00914 request->startup_id = id.id(); 00915 if (dpy == NULL) { 00916 cancel_service_startup_info( request, startup_id, envs ); 00917 return; 00918 } 00919 00920 request->startup_dpy = dpy_str; 00921 00922 KStartupInfoData data; 00923 data.setName( service->name()); 00924 data.setIcon( service->icon()); 00925 data.setDescription( i18n( "Launching %1" , service->name())); 00926 if( !wmclass.isEmpty()) 00927 data.setWMClass( wmclass ); 00928 if( silent ) 00929 data.setSilent( KStartupInfoData::Yes ); 00930 data.setApplicationId( service->entryPath()); 00931 // the rest will be sent by kdeinit 00932 KStartupInfo::sendStartupX( dpy, id, data ); 00933 if( mCached_dpy != dpy && mCached_dpy != NULL ) 00934 XCloseDisplay( mCached_dpy ); 00935 mCached_dpy = dpy; 00936 return; 00937 #else 00938 return; 00939 #endif 00940 } 00941 00942 void 00943 KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QByteArray& startup_id, 00944 const QStringList &envs ) 00945 { 00946 #ifdef Q_WS_X11 00947 if( request != NULL ) 00948 request->startup_id = "0"; // krazy:exclude=doublequote_chars 00949 if( !startup_id.isEmpty() && startup_id != "0" ) 00950 { 00951 QString dpy_str; 00952 foreach (const QString &env, envs) { 00953 if (env.startsWith(QLatin1String("DISPLAY="))) 00954 dpy_str = env.mid(8); 00955 } 00956 Display* dpy = NULL; 00957 if( !dpy_str.isEmpty() && mCached_dpy != NULL 00958 && dpy_str != QLatin1String(XDisplayString( mCached_dpy )) ) 00959 dpy = mCached_dpy; 00960 if( dpy == NULL ) 00961 dpy = XOpenDisplay( dpy_str.toLatin1().constData() ); 00962 if( dpy == NULL ) 00963 return; 00964 KStartupInfoId id; 00965 id.initId(startup_id); 00966 KStartupInfo::sendFinishX( dpy, id ); 00967 if( mCached_dpy != dpy && mCached_dpy != NULL ) 00968 XCloseDisplay( mCached_dpy ); 00969 mCached_dpy = dpy; 00970 } 00971 #endif 00972 } 00973 00974 bool 00975 KLauncher::kdeinit_exec(const QString &app, const QStringList &args, 00976 const QString& workdir, const QStringList &envs, 00977 const QString &startup_id, bool wait, const QDBusMessage &msg) 00978 { 00979 KLaunchRequest *request = new KLaunchRequest; 00980 request->autoStart = false; 00981 request->arg_list = args; 00982 request->name = app; 00983 if (wait) 00984 request->dbus_startup_type = KService::DBusWait; 00985 else 00986 request->dbus_startup_type = KService::DBusNone; 00987 request->pid = 0; 00988 #ifdef Q_WS_X11 00989 request->startup_id = startup_id.toLocal8Bit(); 00990 #endif 00991 request->envs = envs; 00992 request->cwd = workdir; 00993 #ifdef Q_WS_X11 00994 if (!app.endsWith(QLatin1String("kbuildsycoca4"))) { // avoid stupid loop 00995 // Find service, if any - strip path if needed 00996 const QString desktopName = app.mid(app.lastIndexOf(QLatin1Char('/')) + 1); 00997 KService::Ptr service = KService::serviceByDesktopName(desktopName); 00998 if (service) 00999 send_service_startup_info(request, service, 01000 request->startup_id, envs); 01001 else // no .desktop file, no startup info 01002 cancel_service_startup_info(request, request->startup_id, envs); 01003 } 01004 #endif 01005 msg.setDelayedReply(true); 01006 request->transaction = msg; 01007 queueRequest(request); 01008 return true; 01009 } 01010 01011 void 01012 KLauncher::queueRequest(KLaunchRequest *request) 01013 { 01014 requestQueue.append( request ); 01015 if (!bProcessingQueue) 01016 { 01017 bProcessingQueue = true; 01018 QTimer::singleShot(0, this, SLOT( slotDequeue() )); 01019 } 01020 } 01021 01022 void 01023 KLauncher::slotDequeue() 01024 { 01025 do { 01026 KLaunchRequest *request = requestQueue.takeFirst(); 01027 // process request 01028 request->status = KLaunchRequest::Launching; 01029 requestStart(request); 01030 if (request->status != KLaunchRequest::Launching) 01031 { 01032 // Request handled. 01033 #ifdef KLAUNCHER_VERBOSE_OUTPUT 01034 kDebug(7016) << "Request handled already"; 01035 #endif 01036 requestDone( request ); 01037 continue; 01038 } 01039 } while(requestQueue.count()); 01040 bProcessingQueue = false; 01041 } 01042 01043 void 01044 KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service , 01045 const QStringList &urls) 01046 { 01047 const QStringList params = KRun::processDesktopExec(*service, urls); 01048 01049 for(QStringList::ConstIterator it = params.begin(); 01050 it != params.end(); ++it) 01051 { 01052 request->arg_list.append(*it); 01053 } 01054 request->cwd = service->path(); 01055 } 01056 01058 01059 pid_t 01060 KLauncher::requestHoldSlave(const KUrl &url, const QString &app_socket) 01061 { 01062 IdleSlave *slave = 0; 01063 foreach (IdleSlave *p, mSlaveList) 01064 { 01065 if (p->onHold(url)) 01066 { 01067 slave = p; 01068 break; 01069 } 01070 } 01071 if (slave) 01072 { 01073 mSlaveList.removeAll(slave); 01074 slave->connect(app_socket); 01075 return slave->pid(); 01076 } 01077 return 0; 01078 } 01079 01080 pid_t 01081 KLauncher::requestSlave(const QString &protocol, 01082 const QString &host, 01083 const QString &app_socket, 01084 QString &error) 01085 { 01086 IdleSlave *slave = 0; 01087 foreach (IdleSlave *p, mSlaveList) 01088 { 01089 if (p->match(protocol, host, true)) 01090 { 01091 slave = p; 01092 break; 01093 } 01094 } 01095 if (!slave) 01096 { 01097 foreach (IdleSlave *p, mSlaveList) 01098 { 01099 if (p->match(protocol, host, false)) 01100 { 01101 slave = p; 01102 break; 01103 } 01104 } 01105 } 01106 if (!slave) 01107 { 01108 foreach (IdleSlave *p, mSlaveList) 01109 { 01110 if (p->match(protocol, QString(), false)) 01111 { 01112 slave = p; 01113 break; 01114 } 01115 } 01116 } 01117 if (slave) 01118 { 01119 mSlaveList.removeAll(slave); 01120 slave->connect(app_socket); 01121 return slave->pid(); 01122 } 01123 01124 QString name = KProtocolInfo::exec(protocol); 01125 if (name.isEmpty()) 01126 { 01127 error = i18n("Unknown protocol '%1'.\n", protocol); 01128 return 0; 01129 } 01130 01131 QStringList arg_list; 01132 #ifdef Q_WS_WIN 01133 arg_list << name; 01134 arg_list << protocol; 01135 arg_list << mConnectionServer.address(); 01136 arg_list << app_socket; 01137 name = KStandardDirs::findExe(QLatin1String("kioslave")); 01138 #else 01139 QString arg1 = protocol; 01140 QString arg2 = mConnectionServer.address(); 01141 QString arg3 = app_socket; 01142 arg_list.append(arg1); 01143 arg_list.append(arg2); 01144 arg_list.append(arg3); 01145 #endif 01146 01147 kDebug(7016) << "KLauncher: launching new slave " << name << " with protocol=" << protocol 01148 << " args=" << arg_list << endl; 01149 01150 #ifdef Q_OS_UNIX 01151 if (mSlaveDebug == arg1) 01152 { 01153 klauncher_header request_header; 01154 request_header.cmd = LAUNCHER_DEBUG_WAIT; 01155 request_header.arg_length = 0; 01156 write(kdeinitSocket, &request_header, sizeof(request_header)); 01157 } 01158 if (mSlaveValgrind == arg1) { 01159 KLibrary lib(name, KGlobal::mainComponent()); 01160 arg_list.prepend(lib.fileName()); 01161 arg_list.prepend(KStandardDirs::locate("exe", QString::fromLatin1("kioslave"))); 01162 name = QString::fromLatin1("valgrind"); 01163 if (!mSlaveValgrindSkin.isEmpty()) { 01164 arg_list.prepend(QLatin1String("--tool=") + mSlaveValgrindSkin); 01165 } else 01166 arg_list.prepend(QLatin1String("--tool=memcheck")); 01167 } 01168 #endif 01169 KLaunchRequest *request = new KLaunchRequest; 01170 request->autoStart = false; 01171 request->name = name; 01172 request->arg_list = arg_list; 01173 request->dbus_startup_type = KService::DBusNone; 01174 request->pid = 0; 01175 #ifdef Q_WS_X11 01176 request->startup_id = "0"; // krazy:exclude=doublequote_chars 01177 #endif 01178 request->status = KLaunchRequest::Launching; 01179 requestStart(request); 01180 pid_t pid = request->pid; 01181 01182 // kDebug(7016) << "Slave launched, pid = " << pid; 01183 01184 // We don't care about this request any longer.... 01185 requestDone(request); 01186 if (!pid) 01187 { 01188 error = i18n("Error loading '%1'.\n", name); 01189 } 01190 return pid; 01191 } 01192 01193 void 01194 KLauncher::waitForSlave(int pid, const QDBusMessage &msg) 01195 { 01196 foreach (IdleSlave *slave, mSlaveList) 01197 { 01198 if (slave->pid() == static_cast<pid_t>(pid)) 01199 return; // Already here. 01200 } 01201 SlaveWaitRequest *waitRequest = new SlaveWaitRequest; 01202 msg.setDelayedReply(true); 01203 waitRequest->transaction = msg; 01204 waitRequest->pid = static_cast<pid_t>(pid); 01205 mSlaveWaitRequest.append(waitRequest); 01206 } 01207 01208 void 01209 KLauncher::acceptSlave() 01210 { 01211 IdleSlave *slave = new IdleSlave(this); 01212 mConnectionServer.setNextPendingConnection(&slave->mConn); 01213 mSlaveList.append(slave); 01214 connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone())); 01215 connect(slave, SIGNAL(statusUpdate(IdleSlave *)), 01216 this, SLOT(slotSlaveStatus(IdleSlave *))); 01217 if (!mTimer.isActive()) 01218 { 01219 mTimer.start(1000*10); 01220 } 01221 } 01222 01223 void 01224 KLauncher::slotSlaveStatus(IdleSlave *slave) 01225 { 01226 QMutableListIterator<SlaveWaitRequest *> it(mSlaveWaitRequest); 01227 while(it.hasNext()) 01228 { 01229 SlaveWaitRequest *waitRequest = it.next(); 01230 if (waitRequest->pid == slave->pid()) 01231 { 01232 QDBusConnection::sessionBus().send(waitRequest->transaction.createReply()); 01233 it.remove(); 01234 delete waitRequest; 01235 } 01236 } 01237 } 01238 01239 void 01240 KLauncher::slotSlaveGone() 01241 { 01242 IdleSlave *slave = (IdleSlave *) sender(); 01243 mSlaveList.removeAll(slave); 01244 if ((mSlaveList.count() == 0) && (mTimer.isActive())) 01245 { 01246 mTimer.stop(); 01247 } 01248 } 01249 01250 void 01251 KLauncher::idleTimeout() 01252 { 01253 bool keepOneFileSlave=true; 01254 time_t now = time(0); 01255 foreach (IdleSlave *slave, mSlaveList) 01256 { 01257 if ((slave->protocol()==QLatin1String("file")) && (keepOneFileSlave)) 01258 keepOneFileSlave=false; 01259 else if (slave->age(now) > SLAVE_MAX_IDLE) 01260 { 01261 // killing idle slave 01262 delete slave; 01263 } 01264 } 01265 } 01266 01267 void KLauncher::reparseConfiguration() 01268 { 01269 KProtocolManager::reparseConfiguration(); 01270 foreach (IdleSlave *slave, mSlaveList) 01271 slave->reparseConfiguration(); 01272 } 01273 01274 #ifdef Q_WS_WIN 01275 void 01276 KLauncher::slotGotOutput() 01277 { 01278 KProcess *p = static_cast<KProcess *>(sender()); 01279 QByteArray _stdout = p->readAllStandardOutput(); 01280 kDebug(7016) << _stdout.data(); 01281 } 01282 01283 void 01284 KLauncher::slotFinished(int exitCode, QProcess::ExitStatus exitStatus ) 01285 { 01286 KProcess *p = static_cast<KProcess *>(sender()); 01287 kDebug(7016) << "process finished exitcode=" << exitCode << "exitStatus=" << exitStatus; 01288 01289 foreach (KLaunchRequest *request, requestList) 01290 { 01291 if (request->process == p) 01292 { 01293 #ifdef KLAUNCHER_VERBOSE_OUTPUT 01294 kDebug(7016) << "found KProcess, request done"; 01295 #endif 01296 if (exitCode == 0 && exitStatus == QProcess::NormalExit) 01297 request->status = KLaunchRequest::Done; 01298 else 01299 request->status = KLaunchRequest::Error; 01300 requestDone(request); 01301 request->process = 0; 01302 } 01303 } 01304 delete p; 01305 } 01306 #endif 01307 01308 void KLauncher::terminate_kdeinit() 01309 { 01310 kDebug(7016); 01311 #ifndef Q_WS_WIN 01312 klauncher_header request_header; 01313 request_header.cmd = LAUNCHER_TERMINATE_KDEINIT; 01314 request_header.arg_length = 0; 01315 write(kdeinitSocket, &request_header, sizeof(request_header)); 01316 #endif 01317 } 01318 01319 #include "klauncher.moc"
KDE 4.6 API Reference