• Skip to content
  • Skip to link menu
KDE 4.2 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 // #define KLAUNCHER_VERBOSE_OUTPUT
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    // Send it a SLAVE_STATUS command.
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       // Communication problem with slave.
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 // Overload with (bool) onHold, (KUrl) url.
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    // Timeout!
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); // same as ktoolinvocation.cpp
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       // Severe error!
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     // We don't delete the app here, that's intentional.
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  * Read 'len' bytes from 'sock' into buffer.
00272  * returns -1 on failure, 0 on no data.
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    // in case we get a request to start an application and data arrive
00304    // to kdeinitSocket at the same time, requestStart() will already
00305    // call slotKDEInitData(), so we must check there's still something
00306    // to read, otherwise this would block
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(); // Exit!
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     // We should probably check the exitStatus for the uniqueapp case?
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     // appId just registered, e.g. org.koffice.kword-12345
00415     // Let's see if this is what pendingAppId (e.g. org.koffice.kword or *.kword) was waiting for.
00416 
00417     const QString newAppId = appId.left(appId.lastIndexOf('-')); // strip out the -12345 if present.
00418 
00419     //kDebug() << "appId=" << appId << "newAppId=" << newAppId << "pendingAppId=" << pendingAppId;
00420 
00421     if (pendingAppId.startsWith("*.")) {
00422         const QString pendingName = pendingAppId.mid(2);
00423         const QString appName = newAppId.mid(newAppId.lastIndexOf('.')+1);
00424         //kDebug() << "appName=" << appName;
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       // For unique services check the requested service name first
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          // Done
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    // Loop till we find a service that we can start.
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() ) // null strings can't be sent
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 // process.setEnvironment(envlist);
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    // Send request to kdeinit.
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); // avoid_loops, always false here
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    // Wait for pid to return.
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    // Find service, if any - strip path if needed
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 // no .desktop file, no startup info
00689        cancel_service_startup_info( request, startup_id, envs );
00690 
00691    requestStart(request);
00692    // We don't care about this request any longer....
00693    requestDone(request);
00694 }
00695 
00696 
00697 // KDE5: remove
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    // Find service
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 ); // cancel it if any
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    // Find service
00721    if (QFileInfo(serviceName).isAbsolute() )
00722    {
00723       // Full path
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 ); // cancel it if any
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 ); // cancel it if any
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 ); // cancel it if any
00766       return false;
00767    }
00768    KLaunchRequest *request = new KLaunchRequest;
00769    request->autoStart = autoStart;
00770 
00771    if ((urls.count() > 1) && !service->allowMultipleFiles())
00772    {
00773       // We need to launch the application N times. That sucks.
00774       // We ignore the result for application 2 to N.
00775       // For the first file we launch the application in the
00776       // usual way. The reported result is based on this
00777       // application.
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"; // can't use the same startup_id several times
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    // We must have one argument at least!
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        // Special case for kioexec; if createArgs said we were going to use it,
00810        // then we have to expect a kioexec-PID, not a org.kde.finalapp...
00811        // Testcase: konqueror www.kde.org, RMB on link, open with, kruler.
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    // Request will be handled later.
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     // the rest will be sent by kdeinit
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") ) // avoid stupid loop
00958    {
00959        // Find service, if any - strip path if needed
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 // no .desktop file, no startup info
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       // process request
00990       request->status = KLaunchRequest::Launching;
00991       requestStart(request);
00992       if (request->status != KLaunchRequest::Launching)
00993       {
00994          // Request handled.
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 //    kDebug(7016) << "Slave launched, pid = " << pid;
01146 
01147     // We don't care about this request any longer....
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; // Already here.
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            // killing idle slave
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"

KInit

Skip menu "KInit"
  • Main Page
  • 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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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