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