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

KInit

Skip menu "KInit"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal