• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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 #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 #include <QtCore/QFile>
00034 #ifdef Q_WS_WIN
00035 #include <QtCore/QProcess>
00036 #endif
00037 
00038 #include <kconfig.h>
00039 #include <kdebug.h>
00040 #include <klibloader.h>
00041 #include <klocale.h>
00042 #include <kprotocolmanager.h>
00043 #include <kprotocolinfo.h>
00044 #include <krun.h>
00045 #include <kstandarddirs.h>
00046 #include <ktemporaryfile.h>
00047 #include <kurl.h>
00048 
00049 #include <kio/global.h>
00050 #include <kio/connection.h>
00051 #include <kio/slaveinterface.h>
00052 
00053 #ifdef Q_WS_X11
00054 #include <kstartupinfo.h>
00055 #include <X11/Xlib.h>
00056 #endif
00057 
00058 #include <QtCore/QCoreApplication>
00059 
00060 // Dispose slaves after being idle for SLAVE_MAX_IDLE seconds
00061 #define SLAVE_MAX_IDLE  30
00062 
00063 using namespace KIO;
00064 
00065 IdleSlave::IdleSlave(QObject *parent)
00066     : QObject(parent)
00067 {
00068    QObject::connect(&mConn, SIGNAL(readyRead()), this, SLOT(gotInput()));
00069    // Send it a SLAVE_STATUS command.
00070    mConn.send( CMD_SLAVE_STATUS );
00071    mPid = 0;
00072    mBirthDate = time(0);
00073    mOnHold = false;
00074 }
00075 
00076 template<int T> struct PIDType { typedef pid_t PID_t; } ;
00077 template<> struct PIDType<2> { typedef qint16 PID_t; } ;
00078 template<> struct PIDType<4> { typedef qint32 PID_t; } ;
00079 
00080 void
00081 IdleSlave::gotInput()
00082 {
00083    int cmd;
00084    QByteArray data;
00085    if (mConn.read( &cmd, data) == -1)
00086    {
00087       // Communication problem with slave.
00088       kError(7016) << "SlavePool: No communication with slave." << endl;
00089       delete this;
00090    }
00091    else if (cmd == MSG_SLAVE_ACK)
00092    {
00093       delete this;
00094    }
00095    else if (cmd != MSG_SLAVE_STATUS)
00096    {
00097       kError(7016) << "SlavePool: Unexpected data from slave." << endl;
00098       delete this;
00099    }
00100    else
00101    {
00102       QDataStream stream( data );
00103       PIDType<sizeof(pid_t)>::PID_t stream_pid;
00104       pid_t pid;
00105       QByteArray protocol;
00106       QString host;
00107       qint8 b;
00108       stream >> stream_pid >> protocol >> host >> b;
00109       pid = stream_pid;
00110 // Overload with (bool) onHold, (KUrl) url.
00111       if (!stream.atEnd())
00112       {
00113          KUrl url;
00114          stream >> url;
00115          mOnHold = true;
00116          mUrl = url;
00117       }
00118 
00119       mPid = pid;
00120       mConnected = (b != 0);
00121       mProtocol = QString::fromLatin1(protocol);
00122       mHost = host;
00123       emit statusUpdate(this);
00124    }
00125 }
00126 
00127 void
00128 IdleSlave::connect(const QString &app_socket)
00129 {
00130    QByteArray data;
00131    QDataStream stream( &data, QIODevice::WriteOnly);
00132    stream << app_socket;
00133    mConn.send( CMD_SLAVE_CONNECT, data );
00134    // Timeout!
00135 }
00136 
00137 void
00138 IdleSlave::reparseConfiguration()
00139 {
00140    mConn.send( CMD_REPARSECONFIGURATION );
00141 }
00142 
00143 bool
00144 IdleSlave::match(const QString &protocol, const QString &host, bool connected)
00145 {
00146    if (mOnHold) return false;
00147    if (protocol != mProtocol) return false;
00148    if (host.isEmpty()) return true;
00149    if (host != mHost) return false;
00150    if (!connected) return true;
00151    if (!mConnected) return false;
00152    return true;
00153 }
00154 
00155 bool
00156 IdleSlave::onHold(const KUrl &url)
00157 {
00158    if (!mOnHold) return false;
00159    return (url == mUrl);
00160 }
00161 
00162 int
00163 IdleSlave::age(time_t now)
00164 {
00165    return (int) difftime(now, mBirthDate);
00166 }
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    mAutoTimer.setSingleShot(true);
00176    new KLauncherAdaptor(this);
00177    QDBusConnection::sessionBus().registerObject("/KLauncher", this); // same as ktoolinvocation.cpp
00178 
00179    connect(&mAutoTimer, SIGNAL(timeout()), this, SLOT(slotAutoStart()));
00180    connect(QDBusConnection::sessionBus().interface(),
00181            SIGNAL(serviceOwnerChanged(QString,QString,QString)),
00182            SLOT(slotNameOwnerChanged(QString,QString,QString)));
00183 
00184    mConnectionServer.listenForRemote();
00185    connect(&mConnectionServer, SIGNAL(newConnection()), SLOT(acceptSlave()));
00186    if (!mConnectionServer.isListening())
00187    {
00188       // Severe error!
00189       qDebug("KLauncher: Fatal error, can't create tempfile!");
00190       ::_exit(1);
00191    }
00192 
00193    connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout()));
00194 
00195 #ifndef Q_WS_WIN
00196    kdeinitNotifier = new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read);
00197    connect(kdeinitNotifier, SIGNAL( activated( int )),
00198            this, SLOT( slotKDEInitData( int )));
00199    kdeinitNotifier->setEnabled( true );
00200 #endif
00201    lastRequest = 0;
00202    bProcessingQueue = false;
00203 
00204    mSlaveDebug = getenv("KDE_SLAVE_DEBUG_WAIT");
00205    if (!mSlaveDebug.isEmpty())
00206    {
00207       qWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'", qPrintable(mSlaveDebug));
00208    }
00209    mSlaveValgrind = getenv("KDE_SLAVE_VALGRIND");
00210    if (!mSlaveValgrind.isEmpty())
00211    {
00212       mSlaveValgrindSkin = getenv("KDE_SLAVE_VALGRIND_SKIN");
00213       qWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'", qPrintable(mSlaveValgrind));
00214    }
00215 #ifdef Q_WS_WIN
00216    kDebug(7016) << "LAUNCHER_OK";
00217 #else
00218    klauncher_header request_header;
00219    request_header.cmd = LAUNCHER_OK;
00220    request_header.arg_length = 0;
00221    write(kdeinitSocket, &request_header, sizeof(request_header));
00222 #endif
00223 }
00224 
00225 KLauncher::~KLauncher()
00226 {
00227    close();
00228 }
00229 
00230 void KLauncher::close()
00231 {
00232 #ifdef Q_WS_X11
00233    if( mCached_dpy != NULL )
00234        XCloseDisplay( mCached_dpy );
00235 #endif
00236 }
00237 
00238 void
00239 KLauncher::destruct(int exit_code)
00240 {
00241     if (QCoreApplication::instance()) ((KLauncher*)QCoreApplication::instance())->close();
00242     // We don't delete the app here, that's intentional.
00243     ::_exit(exit_code);
00244 }
00245 
00246 void KLauncher::setLaunchEnv(const QString &name, const QString &value)
00247 {
00248 #ifdef Q_WS_WIN
00249     
00250 #else
00251    klauncher_header request_header;
00252    QByteArray requestData;
00253    requestData.append(name.toLocal8Bit()).append('\0').append(value.toLocal8Bit()).append('\0');
00254    request_header.cmd = LAUNCHER_SETENV;
00255    request_header.arg_length = requestData.size();
00256    write(kdeinitSocket, &request_header, sizeof(request_header));
00257    write(kdeinitSocket, requestData.data(), request_header.arg_length);
00258 #endif
00259 }
00260 
00261 #ifndef Q_WS_WIN
00262 /*
00263  * Read 'len' bytes from 'sock' into buffer.
00264  * returns -1 on failure, 0 on no data.
00265  */
00266 static int
00267 read_socket(int sock, char *buffer, int len)
00268 {
00269   ssize_t result;
00270   int bytes_left = len;
00271   while ( bytes_left > 0)
00272   {
00273      result = read(sock, buffer, bytes_left);
00274      if (result > 0)
00275      {
00276         buffer += result;
00277         bytes_left -= result;
00278      }
00279      else if (result == 0)
00280         return -1;
00281      else if ((result == -1) && (errno != EINTR))
00282         return -1;
00283   }
00284   return 0;
00285 }
00286 
00287 
00288 void
00289 KLauncher::slotKDEInitData(int)
00290 {
00291    klauncher_header request_header;
00292    QByteArray requestData;
00293    if( dontBlockReading )
00294    {
00295    // in case we get a request to start an application and data arrive
00296    // to kdeinitSocket at the same time, requestStart() will already
00297    // call slotKDEInitData(), so we must check there's still something
00298    // to read, otherwise this would block
00299       fd_set in;
00300       timeval tm = { 0, 0 };
00301       FD_ZERO ( &in );
00302       FD_SET( kdeinitSocket, &in );
00303       select( kdeinitSocket + 1, &in, 0, 0, &tm );
00304       if( !FD_ISSET( kdeinitSocket, &in ))
00305          return;
00306    }
00307    dontBlockReading = false;
00308    int result = read_socket(kdeinitSocket, (char *) &request_header,
00309                             sizeof( request_header));
00310    if (result == -1)
00311    {
00312       kDebug(7016) << "Exiting on read_socket errno:" << errno;
00313       ::signal( SIGHUP, SIG_IGN);
00314       ::signal( SIGTERM, SIG_IGN);
00315       destruct(255); // Exit!
00316    }
00317    requestData.resize(request_header.arg_length);
00318    result = read_socket(kdeinitSocket, (char *) requestData.data(),
00319                         request_header.arg_length);
00320 
00321    processRequestReturn(request_header.cmd,requestData);
00322        
00323 }
00324 #endif
00325    
00326 void KLauncher::processRequestReturn(int status, const QByteArray &requestData)
00327 {   
00328    if (status == LAUNCHER_DIED)
00329    {
00330      long *request_data;
00331      request_data = (long *) requestData.data();
00332      processDied(request_data[0], request_data[1]);
00333      return;
00334    }
00335    if (lastRequest && (status == LAUNCHER_OK))
00336    {
00337      long *request_data;
00338      request_data = (long *) requestData.data();
00339      lastRequest->pid = (pid_t) (*request_data);
00340      kDebug(7016).nospace() << lastRequest->name << " (pid " << lastRequest->pid <<
00341         ") up and running.";
00342      switch(lastRequest->dbus_startup_type)
00343      {
00344        case KService::DBusNone:
00345          lastRequest->status = KLaunchRequest::Running;
00346          break;
00347        case KService::DBusUnique:
00348        case KService::DBusWait:
00349        case KService::DBusMulti:
00350          lastRequest->status = KLaunchRequest::Launching;
00351          break;
00352      }
00353      lastRequest = 0;
00354      return;
00355    }
00356    if (lastRequest && (status == LAUNCHER_ERROR))
00357    {
00358      lastRequest->status = KLaunchRequest::Error;
00359      kDebug(7016) << lastRequest->name << " failed." << endl;
00360      if (!requestData.isEmpty())
00361         lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data());
00362      lastRequest = 0;
00363      return;
00364    }
00365 
00366    kWarning(7016)<< "Unexpected request return" << (unsigned int) status;
00367 }
00368 
00369 void
00370 KLauncher::processDied(pid_t pid, long /* exitStatus */)
00371 {
00372    foreach (KLaunchRequest *request, requestList)
00373    {
00374       if (request->pid == pid)
00375       {
00376          if (request->dbus_startup_type == KService::DBusWait)
00377             request->status = KLaunchRequest::Done;
00378          else if ((request->dbus_startup_type == KService::DBusUnique)
00379                   && QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name))
00380             request->status = KLaunchRequest::Running;
00381          else
00382             request->status = KLaunchRequest::Error;
00383          requestDone(request);
00384          return;
00385       }
00386    }
00387 }
00388 
00389 void
00390 KLauncher::slotNameOwnerChanged(const QString &appId, const QString &oldOwner,
00391                                 const QString &newOwner)
00392 {
00393    Q_UNUSED(oldOwner);
00394    if (appId.isEmpty() || newOwner.isEmpty())
00395       return;
00396 
00397    foreach (KLaunchRequest *request, requestList)
00398    {
00399       if (request->status != KLaunchRequest::Launching)
00400          continue;
00401 
00402       // For unique services check the requested service name first
00403       if ((request->dbus_startup_type == KService::DBusUnique) &&
00404           ((appId == request->dbus_name) ||
00405            QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)))
00406       {
00407          request->status = KLaunchRequest::Running;
00408          requestDone(request);
00409          continue;
00410       }
00411 
00412       const QString rAppId = request->dbus_name;
00413       if (rAppId.isEmpty())
00414           return;
00415 
00416       //int l = strlen(rAppId);
00417 
00418       QChar c = appId.length() > rAppId.length() ? appId.at(rAppId.length()) : QChar();
00419       if (appId.startsWith(rAppId) && ((appId.length() == rAppId.length()) ||
00420                   (c == QLatin1Char('-'))))
00421       {
00422          request->dbus_name = appId;
00423          request->status = KLaunchRequest::Running;
00424          requestDone(request);
00425          continue;
00426       }
00427    }
00428 }
00429 
00430 void
00431 KLauncher::autoStart(int phase)
00432 {
00433    if( mAutoStart.phase() >= phase )
00434        return;
00435    mAutoStart.setPhase(phase);
00436    if (phase == 0)
00437       mAutoStart.loadAutoStartList();
00438    mAutoTimer.start(0);
00439 }
00440 
00441 void
00442 KLauncher::slotAutoStart()
00443 {
00444    KService::Ptr s;
00445    do
00446    {
00447       QString service = mAutoStart.startService();
00448       if (service.isEmpty())
00449       {
00450          // Done
00451      if( !mAutoStart.phaseDone())
00452      {
00453         mAutoStart.setPhaseDone();
00454             switch( mAutoStart.phase())
00455                 {
00456                 case 0:
00457                     emit autoStart0Done();
00458                     break;
00459                 case 1:
00460                     emit autoStart1Done();
00461                     break;
00462                 case 2:
00463                     emit autoStart2Done();
00464                     break;
00465                 }
00466      }
00467          return;
00468       }
00469       s = new KService(service);
00470    }
00471    while (!start_service(s, QStringList(), QStringList(), "0", false, true, QDBusMessage()));
00472    // Loop till we find a service that we can start.
00473 }
00474 
00475 void
00476 KLauncher::requestDone(KLaunchRequest *request)
00477 {
00478    if ((request->status == KLaunchRequest::Running) ||
00479        (request->status == KLaunchRequest::Done))
00480    {
00481       requestResult.result = 0;
00482       requestResult.dbusName = request->dbus_name;
00483       requestResult.error = "";
00484       requestResult.pid = request->pid;
00485    }
00486    else
00487    {
00488       requestResult.result = 1;
00489       requestResult.dbusName = "";
00490       requestResult.error = i18n("KDEInit could not launch '%1'.", request->name);
00491       if (!request->errorMsg.isEmpty())
00492          requestResult.error += ":\n" + request->errorMsg;
00493       requestResult.pid = 0;
00494 
00495 #ifdef Q_WS_X11
00496       if (!request->startup_dpy.isEmpty())
00497       {
00498          Display* dpy = NULL;
00499          if( (mCached_dpy != NULL) &&
00500               (request->startup_dpy == XDisplayString( mCached_dpy )))
00501             dpy = mCached_dpy;
00502          if( dpy == NULL )
00503             dpy = XOpenDisplay( request->startup_dpy.toLocal8Bit() );
00504          if( dpy )
00505          {
00506             KStartupInfoId id;
00507             id.initId( request->startup_id.toLocal8Bit() );
00508             KStartupInfo::sendFinishX( dpy, id );
00509             if( mCached_dpy != dpy && mCached_dpy != NULL )
00510                XCloseDisplay( mCached_dpy );
00511             mCached_dpy = dpy;
00512          }
00513       }
00514 #endif
00515    }
00516 
00517    if (request->autoStart)
00518    {
00519       mAutoTimer.start(0);
00520    }
00521 
00522    if (request->transaction.type() != QDBusMessage::InvalidMessage)
00523    {
00524       if ( requestResult.dbusName.isNull() ) // null strings can't be sent
00525           requestResult.dbusName = "";
00526       Q_ASSERT( !requestResult.error.isNull() );
00527       PIDType<sizeof(pid_t)>::PID_t stream_pid = requestResult.pid;
00528       QDBusConnection::sessionBus().send(request->transaction.createReply(QVariantList() << requestResult.result
00529                                      << requestResult.dbusName
00530                                      << requestResult.error
00531                                      << stream_pid));
00532    }
00533    requestList.removeAll( request );
00534    delete request;
00535 }
00536 
00537 static void appendLong(QByteArray &ba, long l)
00538 {
00539    const int sz = ba.size();
00540    ba.resize(sz + sizeof(long));
00541    memcpy(ba.data() + sz, &l, sizeof(long));
00542 }
00543 
00544 void
00545 KLauncher::requestStart(KLaunchRequest *request)
00546 {
00547 #ifdef Q_WS_WIN
00548    requestList.append( request );
00549    lastRequest = request;
00550    
00551    QProcess *process  = new QProcess;
00552    process->setProcessChannelMode(QProcess::MergedChannels);
00553    connect(process ,SIGNAL(readyReadStandardOutput()),this, SLOT(slotGotOutput()) );
00554    processList << process;
00555    
00556 // process.setEnvironment(envlist);
00557    QStringList args;
00558    foreach (QString arg, request->arg_list)
00559       args << arg;
00560 
00561    process->start(request->name,args);
00562         
00563    _PROCESS_INFORMATION* _pid = process->pid();
00564     int pid = _pid ? _pid->dwProcessId : 0;
00565 
00566    if (pid)
00567    {
00568       request->pid = pid;
00569       QByteArray data((char *)&pid, sizeof(int));
00570       processRequestReturn(LAUNCHER_OK,data);
00571    }
00572    else 
00573    {
00574       processRequestReturn(LAUNCHER_ERROR,"");
00575    }
00576    return;
00577 
00578 #else
00579    requestList.append( request );
00580    // Send request to kdeinit.
00581    klauncher_header request_header;
00582    QByteArray requestData;
00583    requestData.reserve(1024);
00584 
00585    appendLong(requestData, request->arg_list.count() + 1);
00586    requestData.append(request->name.toLocal8Bit());
00587    requestData.append('\0');
00588    foreach (QString arg, request->arg_list)
00589        requestData.append(arg.toLocal8Bit()).append('\0');
00590    appendLong(requestData, request->envs.count());
00591    foreach (QString env, request->envs)
00592        requestData.append(env.toLocal8Bit()).append('\0');
00593    appendLong(requestData, 0); // avoid_loops, always false here
00594 #ifdef Q_WS_X11
00595    bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0";
00596    if( startup_notify )
00597        requestData.append(request->startup_id.toLocal8Bit()).append('\0');
00598 #endif
00599    if (!request->cwd.isEmpty())
00600        requestData.append(request->cwd.toLocal8Bit()).append('\0');
00601 
00602 #ifdef Q_WS_X11
00603    request_header.cmd = startup_notify ? LAUNCHER_EXT_EXEC : LAUNCHER_EXEC_NEW;
00604 #else
00605    request_header.cmd = LAUNCHER_EXEC_NEW;
00606 #endif
00607    request_header.arg_length = requestData.length();
00608    write(kdeinitSocket, &request_header, sizeof(request_header));
00609    write(kdeinitSocket, requestData.data(), requestData.length());
00610 
00611    // Wait for pid to return.
00612    lastRequest = request;
00613    dontBlockReading = false;
00614    do {
00615       slotKDEInitData( kdeinitSocket );
00616    }
00617    while (lastRequest != 0);
00618    dontBlockReading = true;
00619 #endif
00620 }
00621 
00622 void KLauncher::exec_blind(const QString &name, const QStringList &arg_list, const QStringList &envs, const QString &startup_id)
00623 {
00624    KLaunchRequest *request = new KLaunchRequest;
00625    request->autoStart = false;
00626    request->name = name;
00627    request->arg_list =  arg_list;
00628    request->dbus_startup_type = KService::DBusNone;
00629    request->pid = 0;
00630    request->status = KLaunchRequest::Launching;
00631    request->envs = envs;
00632    // Find service, if any - strip path if needed
00633    KService::Ptr service = KService::serviceByDesktopName( name.mid( name.lastIndexOf( '/' ) + 1 ));
00634    if (service)
00635        send_service_startup_info( request, service, startup_id, QStringList());
00636    else // no .desktop file, no startup info
00637        cancel_service_startup_info( request, startup_id, envs );
00638 
00639    requestStart(request);
00640    // We don't care about this request any longer....
00641    requestDone(request);
00642 }
00643 
00644 
00645 bool
00646 KLauncher::start_service_by_name(const QString &serviceName, const QStringList &urls,
00647     const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
00648 {
00649    KService::Ptr service;
00650    // Find service
00651    service = KService::serviceByName(serviceName);
00652    if (!service)
00653    {
00654       requestResult.result = ENOENT;
00655       requestResult.error = i18n("Could not find service '%1'.", serviceName);
00656       cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
00657       return false;
00658    }
00659    return start_service(service, urls, envs, startup_id, blind, false, msg);
00660 }
00661 
00662 bool
00663 KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls,
00664     const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
00665 {
00666    KService::Ptr service;
00667    // Find service
00668    if (serviceName.startsWith(QLatin1Char('/')))
00669    {
00670       // Full path
00671       service = new KService(serviceName);
00672    }
00673    else
00674    {
00675       service = KService::serviceByDesktopPath(serviceName);
00676    }
00677    if (!service)
00678    {
00679       requestResult.result = ENOENT;
00680       requestResult.error = i18n("Could not find service '%1'.", serviceName);
00681       cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
00682       return false;
00683    }
00684    return start_service(service, urls, envs, startup_id, blind, false, msg);
00685 }
00686 
00687 bool
00688 KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls,
00689     const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg)
00690 {
00691    KService::Ptr service = KService::serviceByDesktopName(serviceName);
00692    if (!service)
00693    {
00694       requestResult.result = ENOENT;
00695       requestResult.error = i18n("Could not find service '%1'.", serviceName);
00696       cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
00697       return false;
00698    }
00699    return start_service(service, urls, envs, startup_id, blind, false, msg);
00700 }
00701 
00702 bool
00703 KLauncher::start_service(KService::Ptr service, const QStringList &_urls,
00704     const QStringList &envs, const QString &startup_id,
00705     bool blind, bool autoStart, const QDBusMessage &msg)
00706 {
00707    QStringList urls = _urls;
00708    if (!service->isValid())
00709    {
00710       requestResult.result = ENOEXEC;
00711       requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath());
00712       cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
00713       return false;
00714    }
00715    KLaunchRequest *request = new KLaunchRequest;
00716    request->autoStart = autoStart;
00717 
00718    if ((urls.count() > 1) && !service->allowMultipleFiles())
00719    {
00720       // We need to launch the application N times. That sucks.
00721       // We ignore the result for application 2 to N.
00722       // For the first file we launch the application in the
00723       // usual way. The reported result is based on this
00724       // application.
00725       QStringList::ConstIterator it = urls.begin();
00726       for(++it;
00727           it != urls.end();
00728           ++it)
00729       {
00730          QStringList singleUrl;
00731          singleUrl.append(*it);
00732          QString startup_id2 = startup_id;
00733          if( !startup_id2.isEmpty() && startup_id2 != "0" )
00734              startup_id2 = "0"; // can't use the same startup_id several times
00735          start_service( service, singleUrl, envs, startup_id2, true, false, msg);
00736       }
00737       QString firstURL = *(urls.begin());
00738       urls.clear();
00739       urls.append(firstURL);
00740    }
00741    createArgs(request, service, urls);
00742 
00743    // We must have one argument at least!
00744    if (!request->arg_list.count())
00745    {
00746       requestResult.result = ENOEXEC;
00747       requestResult.error = i18n("Service '%1' is malformatted.", service->entryPath());
00748       delete request;
00749       cancel_service_startup_info( NULL, startup_id, envs );
00750       return false;
00751    }
00752 
00753    request->name = request->arg_list.takeFirst();
00754    request->dbus_startup_type =  service->dbusStartupType();
00755 
00756    if ((request->dbus_startup_type == KService::DBusUnique) ||
00757        (request->dbus_startup_type == KService::DBusMulti))
00758    {
00759       QVariant v = service->property("X-DBUS-ServiceName");
00760       if (v.isValid())
00761          request->dbus_name = v.toString().toUtf8();
00762       if (request->dbus_name.isEmpty())
00763       {
00764          request->dbus_name = "org.kde." + QFile::encodeName(KRun::binaryName(service->exec(), true));
00765       }
00766    }
00767 
00768    request->pid = 0;
00769    request->envs = envs;
00770    send_service_startup_info( request, service, startup_id, envs );
00771 
00772    // Request will be handled later.
00773    if (!blind && !autoStart)
00774    {
00775       msg.setDelayedReply(true);
00776       request->transaction = msg;
00777    }
00778    queueRequest(request);
00779    return true;
00780 }
00781 
00782 void
00783 KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QString& startup_id,
00784     const QStringList &envs )
00785 {
00786 #ifdef Q_WS_X11
00787     request->startup_id = "0";
00788     if( startup_id == "0" )
00789         return;
00790     bool silent;
00791     QByteArray wmclass;
00792     if( !KRun::checkStartupNotify( QString(), service.data(), &silent, &wmclass ))
00793         return;
00794     KStartupInfoId id;
00795     id.initId( startup_id.toLatin1() );
00796     QString dpy_str;
00797     foreach (QString env, envs) {
00798         if (env.startsWith(QLatin1String("DISPLAY=")))
00799             dpy_str = env.mid(8);
00800     }
00801     Display* dpy = NULL;
00802     if( !dpy_str.isEmpty() && mCached_dpy != NULL
00803         && dpy_str != QLatin1String(XDisplayString(mCached_dpy)) )
00804         dpy = mCached_dpy;
00805     if( dpy == NULL )
00806         dpy = XOpenDisplay( dpy_str.toLatin1().constData() );
00807     request->startup_id = id.id();
00808     if( dpy == NULL )
00809     {
00810         cancel_service_startup_info( request, startup_id, envs );
00811         return;
00812     }
00813 
00814     request->startup_dpy = dpy_str;
00815 
00816     KStartupInfoData data;
00817     data.setName( service->name());
00818     data.setIcon( service->icon());
00819     data.setDescription( i18n( "Launching %1" ,  service->name()));
00820     if( !wmclass.isEmpty())
00821         data.setWMClass( wmclass );
00822     if( silent )
00823         data.setSilent( KStartupInfoData::Yes );
00824     // the rest will be sent by kdeinit
00825     KStartupInfo::sendStartupX( dpy, id, data );
00826     if( mCached_dpy != dpy && mCached_dpy != NULL )
00827         XCloseDisplay( mCached_dpy );
00828     mCached_dpy = dpy;
00829     return;
00830 #else
00831     return;
00832 #endif
00833 }
00834 
00835 void
00836 KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QString& startup_id,
00837     const QStringList &envs )
00838 {
00839 #ifdef Q_WS_X11
00840     if( request != NULL )
00841         request->startup_id = "0";
00842     if( !startup_id.isEmpty() && startup_id != "0" )
00843     {
00844         QString dpy_str;
00845