diff options
Diffstat (limited to 'kinit/klauncher.cpp')
-rw-r--r-- | kinit/klauncher.cpp | 1420 |
1 files changed, 1420 insertions, 0 deletions
diff --git a/kinit/klauncher.cpp b/kinit/klauncher.cpp new file mode 100644 index 000000000..ae4692d60 --- /dev/null +++ b/kinit/klauncher.cpp @@ -0,0 +1,1420 @@ +/* + This file is part of the KDE libraries + Copyright (c) 1999 Waldo Bastian <bastian@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <signal.h> +#include <sys/time.h> + +#include <qfile.h> + +#include <kconfig.h> +#include <kdebug.h> +#include <klibloader.h> +#include <klocale.h> +#include <kprotocolmanager.h> +#include <kprotocolinfo.h> +#include <krun.h> +#include <kstandarddirs.h> +#include <ktempfile.h> +#include <kurl.h> + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include <kstartupinfo.h> // schroder +#endif + + +#include "kio/global.h" +#include "kio/connection.h" +#include "kio/slaveinterface.h" + +#include "klauncher.h" +#include "klauncher_cmds.h" + +//#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#ifdef Q_WS_X11 +//#undef K_WS_QTONLY +#include <X11/Xlib.h> // schroder +#endif + +// Dispose slaves after being idle for SLAVE_MAX_IDLE seconds +#define SLAVE_MAX_IDLE 30 + +using namespace KIO; + +template class QPtrList<KLaunchRequest>; +template class QPtrList<IdleSlave>; + +IdleSlave::IdleSlave(KSocket *socket) +{ + mConn.init(socket); + mConn.connect(this, SLOT(gotInput())); + mConn.send( CMD_SLAVE_STATUS ); + mPid = 0; + mBirthDate = time(0); + mOnHold = false; +} + +void +IdleSlave::gotInput() +{ + int cmd; + QByteArray data; + if (mConn.read( &cmd, data) == -1) + { + // Communication problem with slave. + kdError(7016) << "SlavePool: No communication with slave." << endl; + delete this; + } + else if (cmd == MSG_SLAVE_ACK) + { + delete this; + } + else if (cmd != MSG_SLAVE_STATUS) + { + kdError(7016) << "SlavePool: Unexpected data from slave." << endl; + delete this; + } + else + { + QDataStream stream( data, IO_ReadOnly ); + pid_t pid; + QCString protocol; + QString host; + Q_INT8 b; + stream >> pid >> protocol >> host >> b; +// Overload with (bool) onHold, (KURL) url. + if (!stream.atEnd()) + { + KURL url; + stream >> url; + mOnHold = true; + mUrl = url; + } + + mPid = pid; + mConnected = (b != 0); + mProtocol = protocol; + mHost = host; + emit statusUpdate(this); + } +} + +void +IdleSlave::connect(const QString &app_socket) +{ + QByteArray data; + QDataStream stream( data, IO_WriteOnly); + stream << app_socket; + mConn.send( CMD_SLAVE_CONNECT, data ); + // Timeout! +} + +void +IdleSlave::reparseConfiguration() +{ + mConn.send( CMD_REPARSECONFIGURATION ); +} + +bool +IdleSlave::match(const QString &protocol, const QString &host, bool connected) +{ + if (mOnHold) return false; + if (protocol != mProtocol) return false; + if (host.isEmpty()) return true; + if (host != mHost) return false; + if (!connected) return true; + if (!mConnected) return false; + return true; +} + +bool +IdleSlave::onHold(const KURL &url) +{ + if (!mOnHold) return false; + return (url == mUrl); +} + +int +IdleSlave::age(time_t now) +{ + return (int) difftime(now, mBirthDate); +} + +KLauncher::KLauncher(int _kdeinitSocket, bool new_startup) + : KApplication( false, false ), // No Styles, No GUI + DCOPObject("klauncher"), + kdeinitSocket(_kdeinitSocket), mAutoStart( new_startup ), + dontBlockReading(false), newStartup( new_startup ) +{ +#ifdef Q_WS_X11 + mCached_dpy = NULL; +#endif + connect(&mAutoTimer, SIGNAL(timeout()), this, SLOT(slotAutoStart())); + requestList.setAutoDelete(true); + mSlaveWaitRequest.setAutoDelete(true); + dcopClient()->setNotifications( true ); + connect(dcopClient(), SIGNAL( applicationRegistered( const QCString &)), + this, SLOT( slotAppRegistered( const QCString &))); + dcopClient()->connectDCOPSignal( "DCOPServer", "", "terminateKDE()", + objId(), "terminateKDE()", false ); + + QString prefix = locateLocal("socket", "klauncher"); + KTempFile domainname(prefix, QString::fromLatin1(".slave-socket")); + if (domainname.status() != 0) + { + // Sever error! + qDebug("KLauncher: Fatal error, can't create tempfile!"); + ::exit(1); + } + mPoolSocketName = domainname.name(); +#ifdef __CYGWIN__ + domainname.close(); + domainname.unlink(); +#endif + mPoolSocket = new KServerSocket(QFile::encodeName(mPoolSocketName)); + connect(mPoolSocket, SIGNAL(accepted( KSocket *)), + SLOT(acceptSlave(KSocket *))); + + connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout())); + + kdeinitNotifier = new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read); + connect(kdeinitNotifier, SIGNAL( activated( int )), + this, SLOT( slotKDEInitData( int ))); + kdeinitNotifier->setEnabled( true ); + lastRequest = 0; + bProcessingQueue = false; + + mSlaveDebug = getenv("KDE_SLAVE_DEBUG_WAIT"); + if (!mSlaveDebug.isEmpty()) + { + qWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'", mSlaveDebug.data()); + } + mSlaveValgrind = getenv("KDE_SLAVE_VALGRIND"); + if (!mSlaveValgrind.isEmpty()) + { + mSlaveValgrindSkin = getenv("KDE_SLAVE_VALGRIND_SKIN"); + qWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'", mSlaveValgrind.data()); + } + klauncher_header request_header; + request_header.cmd = LAUNCHER_OK; + request_header.arg_length = 0; + write(kdeinitSocket, &request_header, sizeof(request_header)); +} + +KLauncher::~KLauncher() +{ + close(); +} + +void KLauncher::close() +{ + if (!mPoolSocketName.isEmpty()) + { + QCString filename = QFile::encodeName(mPoolSocketName); + unlink(filename.data()); + } +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + if( mCached_dpy != NULL ) + XCloseDisplay( mCached_dpy ); +#endif +} + +void +KLauncher::destruct(int exit_code) +{ + if (kapp) ((KLauncher*)kapp)->close(); + // We don't delete kapp here, that's intentional. + ::exit(exit_code); +} + +bool +KLauncher::process(const QCString &fun, const QByteArray &data, + QCString &replyType, QByteArray &replyData) +{ + if ((fun == "exec_blind(QCString,QValueList<QCString>)") + || (fun == "exec_blind(QCString,QValueList<QCString>,QValueList<QCString>,QCString)")) + { + QDataStream stream(data, IO_ReadOnly); + replyType = "void"; + QCString name; + QValueList<QCString> arg_list; + QCString startup_id = "0"; + QValueList<QCString> envs; + stream >> name >> arg_list; + if( fun == "exec_blind(QCString,QValueList<QCString>,QValueList<QCString>,QCString)" ) + stream >> envs >> startup_id; + kdDebug(7016) << "KLauncher: Got exec_blind('" << name << "', ...)" << endl; + exec_blind( name, arg_list, envs, startup_id); + return true; + } + if ((fun == "start_service_by_name(QString,QStringList)") || + (fun == "start_service_by_desktop_path(QString,QStringList)")|| + (fun == "start_service_by_desktop_name(QString,QStringList)")|| + (fun == "kdeinit_exec(QString,QStringList)") || + (fun == "kdeinit_exec_wait(QString,QStringList)") || + (fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString)") || + (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString)")|| + (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString)") || + (fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString,bool)") || + (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString,bool)")|| + (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString,bool)") || + (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>)") || + (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>)") || + (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>,QCString)") || + (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>,QCString)")) + { + QDataStream stream(data, IO_ReadOnly); + bool bNoWait = false; + QString serviceName; + QStringList urls; + QValueList<QCString> envs; + QCString startup_id = ""; + DCOPresult.result = -1; + DCOPresult.dcopName = 0; + DCOPresult.error = QString::null; + DCOPresult.pid = 0; + stream >> serviceName >> urls; + if ((fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString,bool)") || + (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString,bool)")|| + (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString,bool)")) + stream >> envs >> startup_id >> bNoWait; + else if ((fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString)") || + (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString)")|| + (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString)")) + stream >> envs >> startup_id; + else if ((fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>)") || + (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>)")) + stream >> envs; + else if ((fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>,QCString)") || + (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>,QCString)")) + stream >> envs >> startup_id; + bool finished; + if (strncmp(fun, "start_service_by_name(", 22) == 0) + { + kdDebug(7016) << "KLauncher: Got start_service_by_name('" << serviceName << "', ...)" << endl; + finished = start_service_by_name(serviceName, urls, envs, startup_id, bNoWait); + } + else if (strncmp(fun, "start_service_by_desktop_path(", 30) == 0) + { + kdDebug(7016) << "KLauncher: Got start_service_by_desktop_path('" << serviceName << "', ...)" << endl; + finished = start_service_by_desktop_path(serviceName, urls, envs, startup_id, bNoWait); + } + else if (strncmp(fun, "start_service_by_desktop_name(", 30) == 0) + { + kdDebug(7016) << "KLauncher: Got start_service_by_desktop_name('" << serviceName << "', ...)" << endl; + finished = start_service_by_desktop_name(serviceName, urls, envs, startup_id, bNoWait ); + } + else if ((fun == "kdeinit_exec(QString,QStringList)") + || (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>)") + || (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>,QCString)")) + { + kdDebug(7016) << "KLauncher: Got kdeinit_exec('" << serviceName << "', ...)" << endl; + finished = kdeinit_exec(serviceName, urls, envs, startup_id, false); + } + else + { + kdDebug(7016) << "KLauncher: Got kdeinit_exec_wait('" << serviceName << "', ...)" << endl; + finished = kdeinit_exec(serviceName, urls, envs, startup_id, true); + } + if (!finished) + { + replyType = "serviceResult"; + QDataStream stream2(replyData, IO_WriteOnly); + stream2 << DCOPresult.result << DCOPresult.dcopName << DCOPresult.error << DCOPresult.pid; + } + return true; + } + else if (fun == "requestSlave(QString,QString,QString)") + { + QDataStream stream(data, IO_ReadOnly); + QString protocol; + QString host; + QString app_socket; + stream >> protocol >> host >> app_socket; + replyType = "QString"; + QString error; + pid_t pid = requestSlave(protocol, host, app_socket, error); + QDataStream stream2(replyData, IO_WriteOnly); + stream2 << pid << error; + return true; + } + else if (fun == "requestHoldSlave(KURL,QString)") + { + QDataStream stream(data, IO_ReadOnly); + KURL url; + QString app_socket; + stream >> url >> app_socket; + replyType = "pid_t"; + pid_t pid = requestHoldSlave(url, app_socket); + QDataStream stream2(replyData, IO_WriteOnly); + stream2 << pid; + return true; + } + else if (fun == "waitForSlave(pid_t)") + { + QDataStream stream(data, IO_ReadOnly); + pid_t pid; + stream >> pid; + waitForSlave(pid); + replyType = "void"; + return true; + + } + else if (fun == "setLaunchEnv(QCString,QCString)") + { + QDataStream stream(data, IO_ReadOnly); + QCString name; + QCString value; + stream >> name >> value; + setLaunchEnv(name, value); + replyType = "void"; + return true; + } + else if (fun == "reparseConfiguration()") + { + KGlobal::config()->reparseConfiguration(); + kdDebug(7016) << "KLauncher::process : reparseConfiguration" << endl; + KProtocolManager::reparseConfiguration(); + IdleSlave *slave; + for(slave = mSlaveList.first(); slave; slave = mSlaveList.next()) + slave->reparseConfiguration(); + replyType = "void"; + return true; + } + else if (fun == "terminateKDE()") + { + ::signal( SIGHUP, SIG_IGN); + ::signal( SIGTERM, SIG_IGN); + kdDebug() << "KLauncher::process ---> terminateKDE" << endl; + klauncher_header request_header; + request_header.cmd = LAUNCHER_TERMINATE_KDE; + request_header.arg_length = 0; + write(kdeinitSocket, &request_header, sizeof(request_header)); + destruct(0); + } + else if (fun == "autoStart()") + { + kdDebug() << "KLauncher::process ---> autoStart" << endl; + autoStart(1); + replyType = "void"; + return true; + } + else if (fun == "autoStart(int)") + { + kdDebug() << "KLauncher::process ---> autoStart(int)" << endl; + QDataStream stream(data, IO_ReadOnly); + int phase; + stream >> phase; + autoStart(phase); + replyType = "void"; + return true; + } + + if (DCOPObject::process(fun, data, replyType, replyData)) + { + return true; + } + kdWarning(7016) << "Got unknown DCOP function: " << fun << endl; + return false; +} + +QCStringList +KLauncher::interfaces() +{ + QCStringList ifaces = DCOPObject::interfaces(); + ifaces += "KLauncher"; + return ifaces; +} + +QCStringList +KLauncher::functions() +{ + QCStringList funcs = DCOPObject::functions(); + funcs << "void exec_blind(QCString,QValueList<QCString>)"; + funcs << "void exec_blind(QCString,QValueList<QCString>,QValueList<QCString>,QCString)"; + funcs << "serviceResult start_service_by_name(QString,QStringList)"; + funcs << "serviceResult start_service_by_desktop_path(QString,QStringList)"; + funcs << "serviceResult start_service_by_desktop_name(QString,QStringList)"; + funcs << "serviceResult kdeinit_exec(QString,QStringList)"; + funcs << "serviceResult kdeinit_exec_wait(QString,QStringList)"; + funcs << "serviceResult start_service_by_name(QString,QStringList,QValueList<QCString>,QCString)"; + funcs << "serviceResult start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString)"; + funcs << "serviceResult start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString)"; + funcs << "serviceResult start_service_by_name(QString,QStringList,QValueList<QCString>,QCString,bool)"; + funcs << "serviceResult start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString,bool)"; + funcs << "serviceResult start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString,bool)"; + funcs << "serviceResult kdeinit_exec(QString,QStringList,QValueList<QCString>)"; + funcs << "serviceResult kdeinit_exec_wait(QString,QStringList,QValueList<QCString>)"; + funcs << "QString requestSlave(QString,QString,QString)"; + funcs << "pid_t requestHoldSlave(KURL,QString)"; + funcs << "void waitForSlave(pid_t)"; + funcs << "void setLaunchEnv(QCString,QCString)"; + funcs << "void reparseConfiguration()"; +// funcs << "void terminateKDE()"; + funcs << "void autoStart()"; + funcs << "void autoStart(int)"; + return funcs; +} + +void KLauncher::setLaunchEnv(const QCString &name, const QCString &_value) +{ + QCString value(_value); + if (value.isNull()) + value = ""; + klauncher_header request_header; + QByteArray requestData(name.length()+value.length()+2); + memcpy(requestData.data(), name.data(), name.length()+1); + memcpy(requestData.data()+name.length()+1, value.data(), value.length()+1); + request_header.cmd = LAUNCHER_SETENV; + request_header.arg_length = requestData.size(); + write(kdeinitSocket, &request_header, sizeof(request_header)); + write(kdeinitSocket, requestData.data(), request_header.arg_length); +} + +/* + * Read 'len' bytes from 'sock' into buffer. + * returns -1 on failure, 0 on no data. + */ +static int +read_socket(int sock, char *buffer, int len) +{ + ssize_t result; + int bytes_left = len; + while ( bytes_left > 0) + { + result = read(sock, buffer, bytes_left); + if (result > 0) + { + buffer += result; + bytes_left -= result; + } + else if (result == 0) + return -1; + else if ((result == -1) && (errno != EINTR)) + return -1; + } + return 0; +} + + +void +KLauncher::slotKDEInitData(int) +{ + klauncher_header request_header; + QByteArray requestData; + if( dontBlockReading ) + { + // in case we get a request to start an application and data arrive + // to kdeinitSocket at the same time, requestStart() will already + // call slotKDEInitData(), so we must check there's still something + // to read, otherwise this would block + fd_set in; + timeval tm = { 0, 0 }; + FD_ZERO ( &in ); + FD_SET( kdeinitSocket, &in ); + select( kdeinitSocket + 1, &in, 0, 0, &tm ); + if( !FD_ISSET( kdeinitSocket, &in )) + return; + } + dontBlockReading = false; + int result = read_socket(kdeinitSocket, (char *) &request_header, + sizeof( request_header)); + if (result == -1) + { + kdDebug() << "Exiting on read_socket errno: " << errno << endl; + ::signal( SIGHUP, SIG_IGN); + ::signal( SIGTERM, SIG_IGN); + destruct(255); // Exit! + } + requestData.resize(request_header.arg_length); + result = read_socket(kdeinitSocket, (char *) requestData.data(), + request_header.arg_length); + + if (request_header.cmd == LAUNCHER_DIED) + { + long *request_data; + request_data = (long *) requestData.data(); + processDied(request_data[0], request_data[1]); + return; + } + if (lastRequest && (request_header.cmd == LAUNCHER_OK)) + { + long *request_data; + request_data = (long *) requestData.data(); + lastRequest->pid = (pid_t) (*request_data); + kdDebug(7016) << lastRequest->name << " (pid " << lastRequest->pid << + ") up and running." << endl; + switch(lastRequest->dcop_service_type) + { + case KService::DCOP_None: + { + lastRequest->status = KLaunchRequest::Running; + break; + } + + case KService::DCOP_Unique: + { + lastRequest->status = KLaunchRequest::Launching; + break; + } + + case KService::DCOP_Wait: + { + lastRequest->status = KLaunchRequest::Launching; + break; + } + + case KService::DCOP_Multi: + { + lastRequest->status = KLaunchRequest::Launching; + break; + } + } + lastRequest = 0; + return; + } + if (lastRequest && (request_header.cmd == LAUNCHER_ERROR)) + { + lastRequest->status = KLaunchRequest::Error; + if (!requestData.isEmpty()) + lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data()); + lastRequest = 0; + return; + } + + kdWarning(7016) << "Unexpected command from KDEInit (" << (unsigned int) request_header.cmd + << ")" << endl; +} + +void +KLauncher::processDied(pid_t pid, long /* exitStatus */) +{ + KLaunchRequest *request = requestList.first(); + for(; request; request = requestList.next()) + { + if (request->pid == pid) + { + if (request->dcop_service_type == KService::DCOP_Wait) + request->status = KLaunchRequest::Done; + else if ((request->dcop_service_type == KService::DCOP_Unique) && + (dcopClient()->isApplicationRegistered(request->dcop_name))) + request->status = KLaunchRequest::Running; + else + request->status = KLaunchRequest::Error; + requestDone(request); + return; + } + } +} + +void +KLauncher::slotAppRegistered(const QCString &appId) +{ + const char *cAppId = appId.data(); + if (!cAppId) return; + + KLaunchRequest *request = requestList.first(); + KLaunchRequest *nextRequest; + for(; request; request = nextRequest) + { + nextRequest = requestList.next(); + if (request->status != KLaunchRequest::Launching) + continue; + + // For unique services check the requested service name first + if ((request->dcop_service_type == KService::DCOP_Unique) && + ((appId == request->dcop_name) || + dcopClient()->isApplicationRegistered(request->dcop_name))) + { + request->status = KLaunchRequest::Running; + requestDone(request); + continue; + } + + const char *rAppId = request->dcop_name.data(); + if (!rAppId) continue; + + int l = strlen(rAppId); + if ((strncmp(rAppId, cAppId, l) == 0) && + ((cAppId[l] == '\0') || (cAppId[l] == '-'))) + { + request->dcop_name = appId; + request->status = KLaunchRequest::Running; + requestDone(request); + continue; + } + } +} + +void +KLauncher::autoStart(int phase) +{ + if( mAutoStart.phase() >= phase ) + return; + mAutoStart.setPhase(phase); + if( newStartup ) + { + if (phase == 0) + mAutoStart.loadAutoStartList(); + } + else + { + if (phase == 1) + mAutoStart.loadAutoStartList(); + } + mAutoTimer.start(0, true); +} + +void +KLauncher::slotAutoStart() +{ + KService::Ptr s; + do + { + QString service = mAutoStart.startService(); + if (service.isEmpty()) + { + // Done + if( !mAutoStart.phaseDone()) + { + mAutoStart.setPhaseDone(); + // Emit signal + if( newStartup ) + { + QCString autoStartSignal; + autoStartSignal.sprintf( "autoStart%dDone()", mAutoStart.phase()); + emitDCOPSignal(autoStartSignal, QByteArray()); + } + else + { + QCString autoStartSignal( "autoStartDone()" ); + int phase = mAutoStart.phase(); + if ( phase > 1 ) + autoStartSignal.sprintf( "autoStart%dDone()", phase ); + emitDCOPSignal(autoStartSignal, QByteArray()); + } + } + return; + } + s = new KService(service); + } + while (!start_service(s, QStringList(), QValueList<QCString>(), "0", false, true)); + // Loop till we find a service that we can start. +} + +void +KLauncher::requestDone(KLaunchRequest *request) +{ + if ((request->status == KLaunchRequest::Running) || + (request->status == KLaunchRequest::Done)) + { + DCOPresult.result = 0; + DCOPresult.dcopName = request->dcop_name; + DCOPresult.error = QString::null; + DCOPresult.pid = request->pid; + } + else + { + DCOPresult.result = 1; + DCOPresult.dcopName = ""; + DCOPresult.error = i18n("KDEInit could not launch '%1'.").arg(request->name); + if (!request->errorMsg.isEmpty()) + DCOPresult.error += ":\n" + request->errorMsg; + DCOPresult.pid = 0; + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + if (!request->startup_dpy.isEmpty()) + { + Display* dpy = NULL; + if( (mCached_dpy != NULL) && + (request->startup_dpy == XDisplayString( mCached_dpy ))) + dpy = mCached_dpy; + if( dpy == NULL ) + dpy = XOpenDisplay( request->startup_dpy ); + if( dpy ) + { + KStartupInfoId id; + id.initId( request->startup_id ); + KStartupInfo::sendFinishX( dpy, id ); + if( mCached_dpy != dpy && mCached_dpy != NULL ) + XCloseDisplay( mCached_dpy ); + mCached_dpy = dpy; + } + } +#endif + } + + if (request->autoStart) + { + mAutoTimer.start(0, true); + } + + if (request->transaction) + { + QByteArray replyData; + QCString replyType; + replyType = "serviceResult"; + QDataStream stream2(replyData, IO_WriteOnly); + stream2 << DCOPresult.result << DCOPresult.dcopName << DCOPresult.error << DCOPresult.pid; + dcopClient()->endTransaction( request->transaction, + replyType, replyData); + } + requestList.removeRef( request ); +} + +void +KLauncher::requestStart(KLaunchRequest *request) +{ + requestList.append( request ); + // Send request to kdeinit. + klauncher_header request_header; + QByteArray requestData; + int length = 0; + length += sizeof(long); // Nr of. Args + length += request->name.length() + 1; // Cmd + for(QValueList<QCString>::Iterator it = request->arg_list.begin(); + it != request->arg_list.end(); + it++) + { + length += (*it).length() + 1; // Args... + } + length += sizeof(long); // Nr of. envs + for(QValueList<QCString>::ConstIterator it = request->envs.begin(); + it != request->envs.end(); + it++) + { + length += (*it).length() + 1; // Envs... + } + length += sizeof( long ); // avoid_loops +#ifdef Q_WS_X11 + bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0"; + if( startup_notify ) + length += request->startup_id.length() + 1; +#endif + if (!request->cwd.isEmpty()) + length += request->cwd.length() + 1; + + requestData.resize( length ); + + char *p = requestData.data(); + long l = request->arg_list.count()+1; + memcpy(p, &l, sizeof(long)); + p += sizeof(long); + strcpy(p, request->name.data()); + p += strlen(p) + 1; + for(QValueList<QCString>::Iterator it = request->arg_list.begin(); + it != request->arg_list.end(); + it++) + { + strcpy(p, (*it).data()); + p += strlen(p) + 1; + } + l = request->envs.count(); + memcpy(p, &l, sizeof(long)); + p += sizeof(long); + for(QValueList<QCString>::ConstIterator it = request->envs.begin(); + it != request->envs.end(); + it++) + { + strcpy(p, (*it).data()); + p += strlen(p) + 1; + } + l = 0; // avoid_loops, always false here + memcpy(p, &l, sizeof(long)); + p += sizeof(long); +#ifdef Q_WS_X11 + if( startup_notify ) + { + strcpy(p, request->startup_id.data()); + p += strlen( p ) + 1; + } +#endif + if (!request->cwd.isEmpty()) + { + strcpy(p, request->cwd.data()); + p += strlen( p ) + 1; + } +#ifdef Q_WS_X11 + request_header.cmd = startup_notify ? LAUNCHER_EXT_EXEC : LAUNCHER_EXEC_NEW; +#else + request_header.cmd = LAUNCHER_EXEC_NEW; +#endif + request_header.arg_length = length; + write(kdeinitSocket, &request_header, sizeof(request_header)); + write(kdeinitSocket, requestData.data(), request_header.arg_length); + + // Wait for pid to return. + lastRequest = request; + dontBlockReading = false; + do { + slotKDEInitData( kdeinitSocket ); + } + while (lastRequest != 0); + dontBlockReading = true; +} + +void +KLauncher::exec_blind( const QCString &name, const QValueList<QCString> &arg_list, + const QValueList<QCString> &envs, const QCString& startup_id ) +{ + KLaunchRequest *request = new KLaunchRequest; + request->autoStart = false; + request->name = name; + request->arg_list = arg_list; + request->dcop_name = 0; + request->dcop_service_type = KService::DCOP_None; + request->pid = 0; + request->status = KLaunchRequest::Launching; + request->transaction = 0; // No confirmation is send + request->envs = envs; + // Find service, if any - strip path if needed + KService::Ptr service = KService::serviceByDesktopName( name.mid( name.findRev( '/' ) + 1 )); + if (service != NULL) + send_service_startup_info( request, service, + startup_id, QValueList< QCString >()); + else // no .desktop file, no startup info + cancel_service_startup_info( request, startup_id, envs ); + + requestStart(request); + // We don't care about this request any longer.... + requestDone(request); +} + + +bool +KLauncher::start_service_by_name(const QString &serviceName, const QStringList &urls, + const QValueList<QCString> &envs, const QCString& startup_id, bool blind) +{ + KService::Ptr service = 0; + // Find service + service = KService::serviceByName(serviceName); + if (!service) + { + DCOPresult.result = ENOENT; + DCOPresult.error = i18n("Could not find service '%1'.").arg(serviceName); + cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any + return false; + } + return start_service(service, urls, envs, startup_id, blind); +} + +bool +KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, + const QValueList<QCString> &envs, const QCString& startup_id, bool blind) +{ + KService::Ptr service = 0; + // Find service + if (serviceName[0] == '/') + { + // Full path + service = new KService(serviceName); + } + else + { + service = KService::serviceByDesktopPath(serviceName); + } + if (!service) + { + DCOPresult.result = ENOENT; + DCOPresult.error = i18n("Could not find service '%1'.").arg(serviceName); + cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any + return false; + } + return start_service(service, urls, envs, startup_id, blind); +} + +bool +KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, + const QValueList<QCString> &envs, const QCString& startup_id, bool blind) +{ + KService::Ptr service = 0; + // Find service + service = KService::serviceByDesktopName(serviceName); + if (!service) + { + DCOPresult.result = ENOENT; + DCOPresult.error = i18n("Could not find service '%1'.").arg(serviceName); + cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any + return false; + } + return start_service(service, urls, envs, startup_id, blind); +} + +bool +KLauncher::start_service(KService::Ptr service, const QStringList &_urls, + const QValueList<QCString> &envs, const QCString& startup_id, bool blind, bool autoStart) +{ + QStringList urls = _urls; + if (!service->isValid()) + { + DCOPresult.result = ENOEXEC; + DCOPresult.error = i18n("Service '%1' is malformatted.").arg(service->desktopEntryPath()); + cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any + return false; + } + KLaunchRequest *request = new KLaunchRequest; + request->autoStart = autoStart; + + if ((urls.count() > 1) && !service->allowMultipleFiles()) + { + // We need to launch the application N times. That sucks. + // We ignore the result for application 2 to N. + // For the first file we launch the application in the + // usual way. The reported result is based on this + // application. + QStringList::ConstIterator it = urls.begin(); + for(++it; + it != urls.end(); + ++it) + { + QStringList singleUrl; + singleUrl.append(*it); + QCString startup_id2 = startup_id; + if( !startup_id2.isEmpty() && startup_id2 != "0" ) + startup_id2 = "0"; // can't use the same startup_id several times + start_service( service, singleUrl, envs, startup_id2, true); + } + QString firstURL = *(urls.begin()); + urls.clear(); + urls.append(firstURL); + } + createArgs(request, service, urls); + + // We must have one argument at least! + if (!request->arg_list.count()) + { + DCOPresult.result = ENOEXEC; + DCOPresult.error = i18n("Service '%1' is malformatted.").arg(service->desktopEntryPath()); + delete request; + cancel_service_startup_info( NULL, startup_id, envs ); + return false; + } + + request->name = request->arg_list.first(); + request->arg_list.remove(request->arg_list.begin()); + + request->dcop_service_type = service->DCOPServiceType(); + + if ((request->dcop_service_type == KService::DCOP_Unique) || + (request->dcop_service_type == KService::DCOP_Multi)) + { + QVariant v = service->property("X-DCOP-ServiceName"); + if (v.isValid()) + request->dcop_name = v.toString().utf8(); + if (request->dcop_name.isEmpty()) + { + request->dcop_name = QFile::encodeName(KRun::binaryName(service->exec(), true)); + } + } + + request->pid = 0; + request->transaction = 0; + request->envs = envs; + send_service_startup_info( request, service, startup_id, envs ); + + // Request will be handled later. + if (!blind && !autoStart) + { + request->transaction = dcopClient()->beginTransaction(); + } + queueRequest(request); + return true; +} + +void +KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QCString& startup_id, + const QValueList<QCString> &envs ) +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 // KStartup* isn't implemented for Qt/Embedded yet + request->startup_id = "0"; + if( startup_id == "0" ) + return; + bool silent; + QCString wmclass; + if( !KRun::checkStartupNotify( QString::null, service, &silent, &wmclass )) + return; + KStartupInfoId id; + id.initId( startup_id ); + const char* dpy_str = NULL; + for( QValueList<QCString>::ConstIterator it = envs.begin(); + it != envs.end(); + ++it ) + if( strncmp( *it, "DISPLAY=", 8 ) == 0 ) + dpy_str = static_cast< const char* >( *it ) + 8; + Display* dpy = NULL; + if( dpy_str != NULL && mCached_dpy != NULL + && qstrcmp( dpy_str, XDisplayString( mCached_dpy )) == 0 ) + dpy = mCached_dpy; + if( dpy == NULL ) + dpy = XOpenDisplay( dpy_str ); + request->startup_id = id.id(); + if( dpy == NULL ) + { + cancel_service_startup_info( request, startup_id, envs ); + return; + } + + request->startup_dpy = dpy_str; + + KStartupInfoData data; + data.setName( service->name()); + data.setIcon( service->icon()); + data.setDescription( i18n( "Launching %1" ).arg( service->name())); + if( !wmclass.isEmpty()) + data.setWMClass( wmclass ); + if( silent ) + data.setSilent( KStartupInfoData::Yes ); + // the rest will be sent by kdeinit + KStartupInfo::sendStartupX( dpy, id, data ); + if( mCached_dpy != dpy && mCached_dpy != NULL ) + XCloseDisplay( mCached_dpy ); + mCached_dpy = dpy; + return; +#else + return; +#endif +} + +void +KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QCString& startup_id, + const QValueList<QCString> &envs ) +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 // KStartup* isn't implemented for Qt/Embedded yet + if( request != NULL ) + request->startup_id = "0"; + if( !startup_id.isEmpty() && startup_id != "0" ) + { + const char* dpy_str = NULL; + for( QValueList<QCString>::ConstIterator it = envs.begin(); + it != envs.end(); + ++it ) + if( strncmp( *it, "DISPLAY=", 8 ) == 0 ) + dpy_str = static_cast< const char* >( *it ) + 8; + Display* dpy = NULL; + if( dpy_str != NULL && mCached_dpy != NULL + && qstrcmp( dpy_str, XDisplayString( mCached_dpy )) == 0 ) + dpy = mCached_dpy; + if( dpy == NULL ) + dpy = XOpenDisplay( dpy_str ); + if( dpy == NULL ) + return; + KStartupInfoId id; + id.initId( startup_id ); + KStartupInfo::sendFinishX( dpy, id ); + if( mCached_dpy != dpy && mCached_dpy != NULL ) + XCloseDisplay( mCached_dpy ); + mCached_dpy = dpy; + } +#endif +} + +bool +KLauncher::kdeinit_exec(const QString &app, const QStringList &args, + const QValueList<QCString> &envs, QCString startup_id, bool wait) +{ + KLaunchRequest *request = new KLaunchRequest; + request->autoStart = false; + + for(QStringList::ConstIterator it = args.begin(); + it != args.end(); + it++) + { + QString arg = *it; + request->arg_list.append(arg.local8Bit()); + } + + request->name = app.local8Bit(); + + if (wait) + request->dcop_service_type = KService::DCOP_Wait; + else + request->dcop_service_type = KService::DCOP_None; + request->dcop_name = 0; + request->pid = 0; +#ifdef Q_WS_X11 + request->startup_id = startup_id; +#endif + request->envs = envs; + if( app != "kbuildsycoca" ) // avoid stupid loop + { + // Find service, if any - strip path if needed + KService::Ptr service = KService::serviceByDesktopName( app.mid( app.findRev( '/' ) + 1 )); + if (service != NULL) + send_service_startup_info( request, service, + startup_id, QValueList< QCString >()); + else // no .desktop file, no startup info + cancel_service_startup_info( request, startup_id, envs ); + } + request->transaction = dcopClient()->beginTransaction(); + queueRequest(request); + return true; +} + +void +KLauncher::queueRequest(KLaunchRequest *request) +{ + requestQueue.append( request ); + if (!bProcessingQueue) + { + bProcessingQueue = true; + QTimer::singleShot(0, this, SLOT( slotDequeue() )); + } +} + +void +KLauncher::slotDequeue() +{ + do { + KLaunchRequest *request = requestQueue.take(0); + // process request + request->status = KLaunchRequest::Launching; + requestStart(request); + if (request->status != KLaunchRequest::Launching) + { + // Request handled. + requestDone( request ); + continue; + } + } while(requestQueue.count()); + bProcessingQueue = false; +} + +void +KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service , + const QStringList &urls) +{ + QStringList params = KRun::processDesktopExec(*service, urls, false); + + for(QStringList::ConstIterator it = params.begin(); + it != params.end(); ++it) + { + request->arg_list.append((*it).local8Bit()); + } + request->cwd = QFile::encodeName(service->path()); +} + +///// IO-Slave functions + +pid_t +KLauncher::requestHoldSlave(const KURL &url, const QString &app_socket) +{ + IdleSlave *slave; + for(slave = mSlaveList.first(); slave; slave = mSlaveList.next()) + { + if (slave->onHold(url)) + break; + } + if (slave) + { + mSlaveList.removeRef(slave); + slave->connect(app_socket); + return slave->pid(); + } + return 0; +} + + +pid_t +KLauncher::requestSlave(const QString &protocol, + const QString &host, + const QString &app_socket, + QString &error) +{ + IdleSlave *slave; + for(slave = mSlaveList.first(); slave; slave = mSlaveList.next()) + { + if (slave->match(protocol, host, true)) + break; + } + if (!slave) + { + for(slave = mSlaveList.first(); slave; slave = mSlaveList.next()) + { + if (slave->match(protocol, host, false)) + break; + } + } + if (!slave) + { + for(slave = mSlaveList.first(); slave; slave = mSlaveList.next()) + { + if (slave->match(protocol, QString::null, false)) + break; + } + } + if (slave) + { + mSlaveList.removeRef(slave); + slave->connect(app_socket); + return slave->pid(); + } + + QString _name = KProtocolInfo::exec(protocol); + if (_name.isEmpty()) + { + error = i18n("Unknown protocol '%1'.\n").arg(protocol); + return 0; + } + + QCString name = _name.latin1(); // ex: "kio_ftp" + QCString arg1 = protocol.latin1(); + QCString arg2 = QFile::encodeName(mPoolSocketName); + QCString arg3 = QFile::encodeName(app_socket); + QValueList<QCString> arg_list; + arg_list.append(arg1); + arg_list.append(arg2); + arg_list.append(arg3); + +// kdDebug(7016) << "KLauncher: launching new slave " << _name << " with protocol=" << protocol << endl; + if (mSlaveDebug == arg1) + { + klauncher_header request_header; + request_header.cmd = LAUNCHER_DEBUG_WAIT; + request_header.arg_length = 0; + write(kdeinitSocket, &request_header, sizeof(request_header)); + } + if (mSlaveValgrind == arg1) + { + arg_list.prepend(QFile::encodeName(KLibLoader::findLibrary(name))); + arg_list.prepend(QFile::encodeName(locate("exe", "kioslave"))); + name = "valgrind"; + if (!mSlaveValgrindSkin.isEmpty()) { + arg_list.prepend(QCString("--tool=") + mSlaveValgrindSkin); + } else + arg_list.prepend("--tool=memcheck"); + } + + KLaunchRequest *request = new KLaunchRequest; + request->autoStart = false; + request->name = name; + request->arg_list = arg_list; + request->dcop_name = 0; + request->dcop_service_type = KService::DCOP_None; + request->pid = 0; +#ifdef Q_WS_X11 + request->startup_id = "0"; +#endif + request->status = KLaunchRequest::Launching; + request->transaction = 0; // No confirmation is send + requestStart(request); + pid_t pid = request->pid; + +// kdDebug(7016) << "Slave launched, pid = " << pid << endl; + + // We don't care about this request any longer.... + requestDone(request); + if (!pid) + { + error = i18n("Error loading '%1'.\n").arg(name); + } + return pid; +} + +void +KLauncher::waitForSlave(pid_t pid) +{ + IdleSlave *slave; + for(slave = mSlaveList.first(); slave; slave = mSlaveList.next()) + { + if (slave->pid() == pid) + return; // Already here. + } + SlaveWaitRequest *waitRequest = new SlaveWaitRequest; + waitRequest->transaction = dcopClient()->beginTransaction(); + waitRequest->pid = pid; + mSlaveWaitRequest.append(waitRequest); +} + +void +KLauncher::acceptSlave(KSocket *slaveSocket) +{ + IdleSlave *slave = new IdleSlave(slaveSocket); + // Send it a SLAVE_STATUS command. + mSlaveList.append(slave); + connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone())); + connect(slave, SIGNAL(statusUpdate(IdleSlave *)), + this, SLOT(slotSlaveStatus(IdleSlave *))); + if (!mTimer.isActive()) + { + mTimer.start(1000*10); + } +} + +void +KLauncher::slotSlaveStatus(IdleSlave *slave) +{ + SlaveWaitRequest *waitRequest = mSlaveWaitRequest.first(); + while(waitRequest) + { + if (waitRequest->pid == slave->pid()) + { + QByteArray replyData; + QCString replyType; + replyType = "void"; + dcopClient()->endTransaction( waitRequest->transaction, replyType, replyData); + mSlaveWaitRequest.removeRef(waitRequest); + waitRequest = mSlaveWaitRequest.current(); + } + else + { + waitRequest = mSlaveWaitRequest.next(); + } + } +} + +void +KLauncher::slotSlaveGone() +{ + IdleSlave *slave = (IdleSlave *) sender(); + mSlaveList.removeRef(slave); + if ((mSlaveList.count() == 0) && (mTimer.isActive())) + { + mTimer.stop(); + } +} + +void +KLauncher::idleTimeout() +{ + bool keepOneFileSlave=true; + time_t now = time(0); + IdleSlave *slave; + for(slave = mSlaveList.first(); slave; slave = mSlaveList.next()) + { + if ((slave->protocol()=="file") && (keepOneFileSlave)) + keepOneFileSlave=false; + else if (slave->age(now) > SLAVE_MAX_IDLE) + { + // killing idle slave + delete slave; + } + } +} + +#include "klauncher.moc" |