00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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 <klibloader.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
00060 #define SLAVE_MAX_IDLE 30
00061
00062
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
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
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
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
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);
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
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
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
00283
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
00292
00293
00294
00295
00296
00297
00298 fd_set in;
00299 timeval tm = { 30, 0 };
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();
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
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
00432
00433
00434 const QString newAppId = appId.left(appId.lastIndexOf(QLatin1Char('-')));
00435
00436
00437
00438 if (pendingAppId.startsWith(QLatin1String("*."))) {
00439 const QString pendingName = pendingAppId.mid(2);
00440 const QString appName = newAppId.mid(newAppId.lastIndexOf(QLatin1Char('.'))+1);
00441
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
00468 if (request->dbus_startup_type == KService::DBusUnique) {
00469 if ((appId == request->dbus_name) ||
00470 QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) {
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
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
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
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("");
00557 requestResult.pid = request->pid;
00558 }
00559 else
00560 {
00561 requestResult.result = 1;
00562 requestResult.dbusName = QString();
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() )
00598 requestResult.dbusName = QString();
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
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
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);
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
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
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
00717 cancel_service_startup_info( request, startup_id.toLocal8Bit(), envs );
00718
00719 requestStart(request);
00720
00721 requestDone(request);
00722 }
00723
00724
00725
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
00732 service = KService::serviceByName(serviceName);
00733 if (!service)
00734 {
00735 requestResult.result = ENOENT;
00736 requestResult.error = i18n("Could not find service '%1'.", serviceName);
00737 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs );
00738 return false;
00739 }
00740 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg);
00741 }
00742
00743 bool
00744 KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls,
00745 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
00746 {
00747 KService::Ptr service;
00748
00749 if (QFileInfo(serviceName).isAbsolute() && QFile::exists(serviceName))
00750 {
00751
00752 service = new KService(serviceName);
00753 }
00754 else
00755 {
00756 service = KService::serviceByDesktopPath(serviceName);
00757
00758
00759
00760 }
00761 if (!service)
00762 {
00763 requestResult.result = ENOENT;
00764 requestResult.error = i18n("Could not find service '%1'.", serviceName);
00765 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs );
00766 return false;
00767 }
00768 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg);
00769 }
00770
00771 bool
00772 KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls,
00773 const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
00774 {
00775 KService::Ptr service = KService::serviceByDesktopName(serviceName);
00776 if (!service)
00777 {
00778 requestResult.result = ENOENT;
00779 requestResult.error = i18n("Could not find service '%1'.", serviceName);
00780 cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs );
00781 return false;
00782 }
00783 return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg);
00784 }
00785
00786 bool
00787 KLauncher::start_service(KService::Ptr service, const QStringList &_urls,
00788 const QStringList &envs, const QByteArray &startup_id,
00789 bool blind, bool autoStart, const QDBusMessage &msg)
00790 {
00791 QStringList urls = _urls;
00792 bool runPermitted = KDesktopFile::isAuthorizedDesktopFile(service->entryPath());
00793
00794 if (!service->isValid() || !runPermitted)
00795 {
00796 requestResult.result = ENOEXEC;
00797 if (service->isValid())
00798 requestResult.error = i18n("Service '%1' must be executable to run.", service->entryPath());
00799 else
00800 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath());
00801 cancel_service_startup_info( NULL, startup_id, envs );
00802 return false;
00803 }
00804 KLaunchRequest *request = new KLaunchRequest;
00805 request->autoStart = autoStart;
00806
00807 if ((urls.count() > 1) && !service->allowMultipleFiles())
00808 {
00809
00810
00811
00812
00813
00814 QStringList::ConstIterator it = urls.constBegin();
00815 for(++it;
00816 it != urls.constEnd();
00817 ++it)
00818 {
00819 QStringList singleUrl;
00820 singleUrl.append(*it);
00821 QByteArray startup_id2 = startup_id;
00822 if( !startup_id2.isEmpty() && startup_id2 != "0" )
00823 startup_id2 = "0";
00824 start_service( service, singleUrl, envs, startup_id2, true, false, msg);
00825 }
00826 QString firstURL = *(urls.begin());
00827 urls.clear();
00828 urls.append(firstURL);
00829 }
00830 createArgs(request, service, urls);
00831
00832
00833 if (!request->arg_list.count())
00834 {
00835 requestResult.result = ENOEXEC;
00836 requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath());
00837 delete request;
00838 cancel_service_startup_info( NULL, startup_id, envs );
00839 return false;
00840 }
00841
00842 request->name = request->arg_list.takeFirst();
00843
00844 if (request->name.endsWith(QLatin1String("/kioexec"))) {
00845
00846
00847
00848
00849 request->dbus_startup_type = KService::DBusMulti;
00850 request->dbus_name = QString::fromLatin1("org.kde.kioexec");
00851 } else {
00852 request->dbus_startup_type = service->dbusStartupType();
00853
00854 if ((request->dbus_startup_type == KService::DBusUnique) ||
00855 (request->dbus_startup_type == KService::DBusMulti)) {
00856 const QVariant v = service->property(QLatin1String("X-DBUS-ServiceName"));
00857 if (v.isValid()) {
00858 request->dbus_name = v.toString();
00859 }
00860 if (request->dbus_name.isEmpty()) {
00861 const QString binName = KRun::binaryName(service->exec(), true);
00862 request->dbus_name = QString::fromLatin1("org.kde.") + binName;
00863 request->tolerant_dbus_name = QString::fromLatin1("*.") + binName;
00864 }
00865 }
00866 }
00867
00868 #ifdef KLAUNCHER_VERBOSE_OUTPUT
00869 kDebug(7016) << "name=" << request->name << "dbus_name=" << request->dbus_name
00870 << "startup type=" << s_DBusStartupTypeToString[request->dbus_startup_type];
00871 #endif
00872
00873 request->pid = 0;
00874 request->envs = envs;
00875 send_service_startup_info( request, service, startup_id, envs );
00876
00877
00878 if (!blind && !autoStart)
00879 {
00880 msg.setDelayedReply(true);
00881 request->transaction = msg;
00882 }
00883 queueRequest(request);
00884 return true;
00885 }
00886
00887 void
00888 KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QByteArray& startup_id,
00889 const QStringList &envs )
00890 {
00891 #ifdef Q_WS_X11
00892 request->startup_id = "0";
00893 if (startup_id == "0")
00894 return;
00895 bool silent;
00896 QByteArray wmclass;
00897 if( !KRun::checkStartupNotify( QString(), service.data(), &silent, &wmclass ))
00898 return;
00899 KStartupInfoId id;
00900 id.initId(startup_id);
00901 QByteArray dpy_str;
00902 foreach (const QString &env, envs) {
00903 if (env.startsWith(QLatin1String("DISPLAY=")))
00904 dpy_str = env.mid(8).toLocal8Bit();
00905 }
00906 Display* dpy = NULL;
00907 if (!dpy_str.isEmpty() && mCached_dpy != NULL && dpy_str != XDisplayString(mCached_dpy))
00908 dpy = mCached_dpy;
00909 if (dpy == NULL)
00910 dpy = XOpenDisplay(dpy_str);
00911 request->startup_id = id.id();
00912 if (dpy == NULL) {
00913 cancel_service_startup_info( request, startup_id, envs );
00914 return;
00915 }
00916
00917 request->startup_dpy = dpy_str;
00918
00919 KStartupInfoData data;
00920 data.setName( service->name());
00921 data.setIcon( service->icon());
00922 data.setDescription( i18n( "Launching %1" , service->name()));
00923 if( !wmclass.isEmpty())
00924 data.setWMClass( wmclass );
00925 if( silent )
00926 data.setSilent( KStartupInfoData::Yes );
00927
00928 KStartupInfo::sendStartupX( dpy, id, data );
00929 if( mCached_dpy != dpy && mCached_dpy != NULL )
00930 XCloseDisplay( mCached_dpy );
00931 mCached_dpy = dpy;
00932 return;
00933 #else
00934 return;
00935 #endif
00936 }
00937
00938 void
00939 KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QByteArray& startup_id,
00940 const QStringList &envs )
00941 {
00942 #ifdef Q_WS_X11
00943 if( request != NULL )
00944 request->startup_id = "0";
00945 if( !startup_id.isEmpty() && startup_id != "0" )
00946 {
00947 QString dpy_str;
00948 foreach (const QString &env, envs) {
00949 if (env.startsWith(QLatin1String("DISPLAY=")))
00950 dpy_str = env.mid(8);
00951 }
00952 Display* dpy = NULL;
00953 if( !dpy_str.isEmpty() && mCached_dpy != NULL
00954 && dpy_str != QLatin1String(XDisplayString( mCached_dpy )) )
00955 dpy = mCached_dpy;
00956 if( dpy == NULL )
00957 dpy = XOpenDisplay( dpy_str.toLatin1().constData() );
00958 if( dpy == NULL )
00959 return;
00960 KStartupInfoId id;
00961 id.initId(startup_id);
00962 KStartupInfo::sendFinishX( dpy, id );
00963 if( mCached_dpy != dpy && mCached_dpy != NULL )
00964 XCloseDisplay( mCached_dpy );
00965 mCached_dpy = dpy;
00966 }
00967 #endif
00968 }
00969
00970 bool
00971 KLauncher::kdeinit_exec(const QString &app, const QStringList &args,
00972 const QString& workdir, const QStringList &envs,
00973 const QString &startup_id, bool wait, const QDBusMessage &msg)
00974 {
00975 KLaunchRequest *request = new KLaunchRequest;
00976 request->autoStart = false;
00977 request->arg_list = args;
00978 request->name = app;
00979 if (wait)
00980 request->dbus_startup_type = KService::DBusWait;
00981 else
00982 request->dbus_startup_type = KService::DBusNone;
00983 request->pid = 0;
00984 #ifdef Q_WS_X11
00985 request->startup_id = startup_id.toLocal8Bit();
00986 #endif
00987 request->envs = envs;
00988 request->cwd = workdir;
00989 #ifdef Q_WS_X11
00990 if (!app.endsWith(QLatin1String("kbuildsycoca4"))) {
00991
00992 const QString desktopName = app.mid(app.lastIndexOf(QLatin1Char('/')) + 1);
00993 KService::Ptr service = KService::serviceByDesktopName(desktopName);
00994 if (service)
00995 send_service_startup_info(request, service,
00996 request->startup_id, QStringList());
00997 else
00998 cancel_service_startup_info(request, request->startup_id, envs);
00999 }
01000 #endif
01001 msg.setDelayedReply(true);
01002 request->transaction = msg;
01003 queueRequest(request);
01004 return true;
01005 }
01006
01007 void
01008 KLauncher::queueRequest(KLaunchRequest *request)
01009 {
01010 requestQueue.append( request );
01011 if (!bProcessingQueue)
01012 {
01013 bProcessingQueue = true;
01014 QTimer::singleShot(0, this, SLOT( slotDequeue() ));
01015 }
01016 }
01017
01018 void
01019 KLauncher::slotDequeue()
01020 {
01021 do {
01022 KLaunchRequest *request = requestQueue.takeFirst();
01023
01024 request->status = KLaunchRequest::Launching;
01025 requestStart(request);
01026 if (request->status != KLaunchRequest::Launching)
01027 {
01028
01029 #ifdef KLAUNCHER_VERBOSE_OUTPUT
01030 kDebug(7016) << "Request handled already";
01031 #endif
01032 requestDone( request );
01033 continue;
01034 }
01035 } while(requestQueue.count());
01036 bProcessingQueue = false;
01037 }
01038
01039 void
01040 KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service ,
01041 const QStringList &urls)
01042 {
01043 const QStringList params = KRun::processDesktopExec(*service, urls);
01044
01045 for(QStringList::ConstIterator it = params.begin();
01046 it != params.end(); ++it)
01047 {
01048 request->arg_list.append(*it);
01049 }
01050 request->cwd = service->path();
01051 }
01052
01054
01055 pid_t
01056 KLauncher::requestHoldSlave(const KUrl &url, const QString &app_socket)
01057 {
01058 IdleSlave *slave = 0;
01059 foreach (IdleSlave *p, mSlaveList)
01060 {
01061 if (p->onHold(url))
01062 {
01063 slave = p;
01064 break;
01065 }
01066 }
01067 if (slave)
01068 {
01069 mSlaveList.removeAll(slave);
01070 slave->connect(app_socket);
01071 return slave->pid();
01072 }
01073 return 0;
01074 }
01075
01076
01077 pid_t
01078 KLauncher::requestSlave(const QString &protocol,
01079 const QString &host,
01080 const QString &app_socket,
01081 QString &error)
01082 {
01083 IdleSlave *slave = 0;
01084 foreach (IdleSlave *p, mSlaveList)
01085 {
01086 if (p->match(protocol, host, true))
01087 {
01088 slave = p;
01089 break;
01090 }
01091 }
01092 if (!slave)
01093 {
01094 foreach (IdleSlave *p, mSlaveList)
01095 {
01096 if (p->match(protocol, host, false))
01097 {
01098 slave = p;
01099 break;
01100 }
01101 }
01102 }
01103 if (!slave)
01104 {
01105 foreach (IdleSlave *p, mSlaveList)
01106 {
01107 if (p->match(protocol, QString(), false))
01108 {
01109 slave = p;
01110 break;
01111 }
01112 }
01113 }
01114 if (slave)
01115 {
01116 mSlaveList.removeAll(slave);
01117 slave->connect(app_socket);
01118 return slave->pid();
01119 }
01120
01121 QString name = KProtocolInfo::exec(protocol);
01122 if (name.isEmpty())
01123 {
01124 error = i18n("Unknown protocol '%1'.\n", protocol);
01125 return 0;
01126 }
01127
01128 QStringList arg_list;
01129 #ifdef Q_WS_WIN
01130 arg_list << name;
01131 arg_list << protocol;
01132 arg_list << mConnectionServer.address();
01133 arg_list << app_socket;
01134 name = KStandardDirs::findExe(QLatin1String("kioslave"));
01135 #else
01136 QString arg1 = protocol;
01137 QString arg2 = mConnectionServer.address();
01138 QString arg3 = app_socket;
01139 arg_list.append(arg1);
01140 arg_list.append(arg2);
01141 arg_list.append(arg3);
01142 #endif
01143
01144 kDebug(7016) << "KLauncher: launching new slave " << name << " with protocol=" << protocol
01145 << " args=" << arg_list << endl;
01146
01147 #ifdef Q_OS_UNIX
01148 if (mSlaveDebug == arg1)
01149 {
01150 klauncher_header request_header;
01151 request_header.cmd = LAUNCHER_DEBUG_WAIT;
01152 request_header.arg_length = 0;
01153 write(kdeinitSocket, &request_header, sizeof(request_header));
01154 }
01155 if (mSlaveValgrind == arg1)
01156 {
01157 arg_list.prepend(KLibLoader::findLibrary(name));
01158 arg_list.prepend(KStandardDirs::locate("exe", QString::fromLatin1("kioslave")));
01159 name = QString::fromLatin1("valgrind");
01160 if (!mSlaveValgrindSkin.isEmpty()) {
01161 arg_list.prepend(QLatin1String("--tool=") + mSlaveValgrindSkin);
01162 } else
01163 arg_list.prepend(QLatin1String("--tool=memcheck"));
01164 }
01165 #endif
01166 KLaunchRequest *request = new KLaunchRequest;
01167 request->autoStart = false;
01168 request->name = name;
01169 request->arg_list = arg_list;
01170 request->dbus_startup_type = KService::DBusNone;
01171 request->pid = 0;
01172 #ifdef Q_WS_X11
01173 request->startup_id = "0";
01174 #endif
01175 request->status = KLaunchRequest::Launching;
01176 requestStart(request);
01177 pid_t pid = request->pid;
01178
01179
01180
01181
01182 requestDone(request);
01183 if (!pid)
01184 {
01185 error = i18n("Error loading '%1'.\n", name);
01186 }
01187 return pid;
01188 }
01189
01190 void
01191 KLauncher::waitForSlave(int pid, const QDBusMessage &msg)
01192 {
01193 foreach (IdleSlave *slave, mSlaveList)
01194 {
01195 if (slave->pid() == static_cast<pid_t>(pid))
01196 return;
01197 }
01198 SlaveWaitRequest *waitRequest = new SlaveWaitRequest;
01199 msg.setDelayedReply(true);
01200 waitRequest->transaction = msg;
01201 waitRequest->pid = static_cast<pid_t>(pid);
01202 mSlaveWaitRequest.append(waitRequest);
01203 }
01204
01205 void
01206 KLauncher::acceptSlave()
01207 {
01208 IdleSlave *slave = new IdleSlave(this);
01209 mConnectionServer.setNextPendingConnection(&slave->mConn);
01210 mSlaveList.append(slave);
01211 connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone()));
01212 connect(slave, SIGNAL(statusUpdate(IdleSlave *)),
01213 this, SLOT(slotSlaveStatus(IdleSlave *)));
01214 if (!mTimer.isActive())
01215 {
01216 mTimer.start(1000*10);
01217 }
01218 }
01219
01220 void
01221 KLauncher::slotSlaveStatus(IdleSlave *slave)
01222 {
01223 QMutableListIterator<SlaveWaitRequest *> it(mSlaveWaitRequest);
01224 while(it.hasNext())
01225 {
01226 SlaveWaitRequest *waitRequest = it.next();
01227 if (waitRequest->pid == slave->pid())
01228 {
01229 QDBusConnection::sessionBus().send(waitRequest->transaction.createReply());
01230 it.remove();
01231 delete waitRequest;
01232 }
01233 }
01234 }
01235
01236 void
01237 KLauncher::slotSlaveGone()
01238 {
01239 IdleSlave *slave = (IdleSlave *) sender();
01240 mSlaveList.removeAll(slave);
01241 if ((mSlaveList.count() == 0) && (mTimer.isActive()))
01242 {
01243 mTimer.stop();
01244 }
01245 }
01246
01247 void
01248 KLauncher::idleTimeout()
01249 {
01250 bool keepOneFileSlave=true;
01251 time_t now = time(0);
01252 foreach (IdleSlave *slave, mSlaveList)
01253 {
01254 if ((slave->protocol()==QLatin1String("file")) && (keepOneFileSlave))
01255 keepOneFileSlave=false;
01256 else if (slave->age(now) > SLAVE_MAX_IDLE)
01257 {
01258
01259 delete slave;
01260 }
01261 }
01262 }
01263
01264 void KLauncher::reparseConfiguration()
01265 {
01266 KProtocolManager::reparseConfiguration();
01267 foreach (IdleSlave *slave, mSlaveList)
01268 slave->reparseConfiguration();
01269 }
01270
01271 #ifdef Q_WS_WIN
01272 void
01273 KLauncher::slotGotOutput()
01274 {
01275 KProcess *p = static_cast<KProcess *>(sender());
01276 QByteArray _stdout = p->readAllStandardOutput();
01277 kDebug(7016) << _stdout.data();
01278 }
01279
01280 void
01281 KLauncher::slotFinished(int exitCode, QProcess::ExitStatus exitStatus )
01282 {
01283 KProcess *p = static_cast<KProcess *>(sender());
01284 kDebug(7016) << "process finished exitcode=" << exitCode << "exitStatus=" << exitStatus;
01285
01286 foreach (KLaunchRequest *request, requestList)
01287 {
01288 if (request->process == p)
01289 {
01290 #ifdef KLAUNCHER_VERBOSE_OUTPUT
01291 kDebug(7016) << "found KProcess, request done";
01292 #endif
01293 if (exitCode == 0 && exitStatus == QProcess::NormalExit)
01294 request->status = KLaunchRequest::Done;
01295 else
01296 request->status = KLaunchRequest::Error;
01297 requestDone(request);
01298 request->process = 0;
01299 }
01300 }
01301 delete p;
01302 }
01303 #endif
01304
01305 void KLauncher::terminate_kdeinit()
01306 {
01307 kDebug(7016);
01308 #ifndef Q_WS_WIN
01309 klauncher_header request_header;
01310 request_header.cmd = LAUNCHER_TERMINATE_KDEINIT;
01311 request_header.arg_length = 0;
01312 write(kdeinitSocket, &request_header, sizeof(request_header));
01313 #endif
01314 }
01315
01316 #include "klauncher.moc"